React歌词滚动效果(跟随音乐播放时间滚动)

首先给audio绑定更新时间事件

复制代码
	const updateTime = e => {
		console.log(e.target.currentTime)
		setCurrentTime(e.target.currentTime);
	};


	<audio
				src={currentSong.url}
				ref={audio}
				onCanPlay={ready}
				onEnded={end}
				onTimeUpdate={updateTime}
			></audio>

当歌曲播放时间改变的时候会触发updateTime事件,如下所示

歌词json格式

复制代码
[
    {
        "time": 2,
        "content": "采样曲:願い~あの頃のキミへ~",
        "contents": [
            "采样曲:願い~あの頃のキミへ~"
        ]
    },
    {
        "time": 12,
        "content": "中文填词:一只然",
        "contents": [
            "中文填词:一只然"
        ]
    },
    {
        "time": 15,
        "content": "OP(原属词曲版权公司):テレビ東京ミュージック 东京电视台音乐",
        "contents": [
            "OP(原属词曲版权公司):テレビ東京ミュージック 东京电视台音乐"
        ]
    },
    {
        "time": 19,
        "content": "本作品经过原词曲作者以及版权公司授权",
        "contents": [
            "本作品经过原词曲作者以及版权公司授权"
        ]
    },
    ......
    ]

接下来就是根据当前的播放时间显示歌词高亮,给歌词绑定高亮放大样式

复制代码
.highlight {
color: $theme-color;
font-weight: $font-weight-bold;
font-size: 16px !important;
}

// 使用Redux的useSelector获取当前播放时间
const currentTime = useSelector(state => state.musicReducer.currentTime);

// 使用React的useMemo优化性能,只有当currentTime变化时,才会重新计算time的值
const time= useMemo(() => {
    return currentTime;
},[currentTime]);

// updateTime函数用于更新当前歌词的索引
const updateTime = e => {
    // 在所有歌词中找到第一个时间大于当前播放时间的歌词,其前一个歌词就是当前应该显示的歌词
    const currentLyricIndex = lyric.findIndex((lyricItem, index) => {
        // 判断是否是最后一项歌词,如果是,下一项歌词的时间设为无穷大
        const isLastItem = index === lyric.length - 1;
        const nextLyricTime = isLastItem ? Infinity : lyric[index + 1].time;
        // 如果当前播放时间在当前歌词和下一条歌词的时间之间,说明当前歌词应该被显示
        return time >= lyricItem.time && time < nextLyricTime;
    });
    // 更新当前歌词的索引
    setCurrentLyricIndex(currentLyricIndex);
};

// 使用React的useEffect在time变化时,调用updateTime函数,更新当前歌词的索引
useEffect(() => {
    updateTime()
}, [time]);

time发生变化时,调用updateTime函数来更新当前歌词的索引currentLyricIndex。确保在歌曲播放过程中,歌词随着时间的推移而更新。

最后,实现歌词滚动的效果

  1. 创建 scrollRef

    复制代码
    const scrollRef = useRef();

    使用 useRef 创建了一个 scrollRef,用于引用 Scroll 组件的实例。

  2. 使用 useEffect 进行歌词滚动:

    复制代码
        useEffect(() => {
            // 模拟异步加载歌词
            // 假设你要滚动到的歌词元素有一个特定的类名 ".lyric-item.highlight"
            const selector = '.lyric-item.highlight';
            // 调用 Scroll 组件的 scrollToElement 方法
            if (scrollRef.current) {
                scrollRef.current.scrollToElement(selector, 500); // 第二个参数是滚动时间,可以根据需要调整
            }
        }, [currentLyricIndex]);

    currentLyricIndex 发生变化时,useEffect 会被触发。在该效果中,它模拟异步加载歌词,然后通过 scrollRef.current.scrollToElement 方法滚动到指定的歌词元素,滚动时间为500毫秒。这样,每次歌词发生变化时都会滚动到当前高亮的歌词位置。

  3. Scroll 组件的 scrollToElement 方法实现:

    复制代码
            scrollToElement(selector, time = 0) {
                if (bScroll) {
                    const targetElement = document.querySelector(selector);
                    if (targetElement) {
                        const containerHeight = scrollContainerRef.current.clientHeight;
                        const targetHeight = targetElement.clientHeight;
                        const offsetTop = (containerHeight - targetHeight) / 2;
                        bScroll.scrollToElement(targetElement, time, 0, -offsetTop);
                    }
                }
            }

    这是 Scroll 组件内部的 scrollToElement 方法的实现。首先,通过 document.querySelector(selector) 获取到目标元素(具有指定类名的高亮歌词元素)。然后,计算目标元素相对于滚动容器的偏移,最后使用 bScroll.scrollToElement 将目标元素滚动到可视区域,传入的参数包括时间、水平和垂直的偏移。

相关推荐
钛态3 小时前
Vite 中的 CSS 工程化:从 CSS Modules 到 UnoCSS 的渐进式迁移
前端·vue·react·web
Csvn5 小时前
原型链、this 与闭包内存:三座深水区一次打通(含内存泄漏排查实战)
前端·javascript
葡萄城技术团队5 小时前
如何通过 JSON 数据源 / Web API 快速构建轻量级报表(Report)?
前端·json·原型模式
万维易源5 小时前
趣味项目素材|免费脑筋急转弯查询接口实战接入教程
javascript·免费api·脑筋急转弯查询·脑筋急转弯接口
浪兎兎5 小时前
【Java Web】Servlet + JSP 笔记
java·前端·servlet
Patrick_Wilson6 小时前
iOS Safari Clipboard API 权限弹窗问题
前端·ios·safari
OpenTiny社区7 小时前
Naive UI × GenUI SDK:自定义物料库搭建实战
前端·ai编程
kyriewen7 小时前
我拿 4 个真实前端任务试了 GLM-5.3 Flash:代码一遍跑通,账单 4 分钱
前端·程序员·ai编程
IT_陈寒7 小时前
Vue的响应式更新有时候真的不听话
前端·人工智能·后端