梦想做个厨子,结果成了码农

听久了云村音乐,加上自己是个前端,于是想尝试造个轮子。

歌词同步

实现难点

歌词是这般的

[00:18.640]这街上太拥挤
[00:20.820]太多人有秘密

于是你要基于前面的时间,在与当前播放的时间去做对比,选出下一句歌词。
仔细想想好像也就那回事。

实现方式

  • 基于 timeupdate 的时间去判断
  • 基于时间戳,然后利用 setTimeout 标准延迟(delay多少就是多少)

具体实现

第一种

audio.addEventListener('timeupdate',function(){
    
    let liveTime = parseFloat((audio.currentTime).toFixed(2))// + 0.2;

    let _next = timeArr.findIndex((item, idx) => {
        if(liveTime >= (item - offset) && liveTime < (timeArr[idx + 1] - offset)){
            return true
        }
        // 开局歌词不是从零开始 , 或者结尾的时候
        if(!timeArr[idx + 1] || liveTime < item){
            return true
        }
        return idx === timeArr.length
    })
    if(_next !== -1) {
        let ele = $('.Lyrics').children(`p:eq(${_next})`)
        if(!curIndex || (curIndex && ele.html() !== curIndex.html())){
            lyrSroll(ele)
        }
    } 
 })

当currentTime更新时会触发timeupdate事件,简单理解为在播放的时候,事件就会一直被触发。

这个事件的触发频率由系统决定,但是会保证每秒触发4-66次(前提是每次事件处理不会超过250ms)。

换句话说,就是一秒内执行的次数,不固定。引起的后果就是,有的歌词快点出来,有的慢点。

所以爆肝了,over。

第二种

function _playRest() {
    let line = timeArr[curNum]
    let delay = line - (Date.now() - startStamp)
    timer = setTimeout(() => {
        lyrSroll($('.Lyrics').children(`p:eq(${curNum++})`))
      if (curNum < timeArr.length && !audio.paused) {
        _playRest()
      }
    }, delay)
  }

这种是基于 取歌词时的时间戳 - 开始播放的时间戳 = 歌曲已经开始了的时间。然后就可以很帅气的使用 歌词的时间 - 歌曲已经开始的时间,即这个歌词延迟多久出现。
开发后,还是这个完美一些。

最后附上一个大概算是最终版吧,传送门

参考资料
1.https://developer.mozilla.org/zh-CN/docs/Web/Events/timeupdate
2.https://github.com/ustbhuangyi/lyric-parser