vue歌词解析与歌词滚动

475 阅读3分钟

播放标签

<audio id="songPlayAudio" :src="playUrl" ref="songPlayAudioRef" controls style="display: none;"
  @ended="onended"></audio>

歌词滚动区,需要在解析歌词后,根据播放时间高亮对应歌词并滚动

  < !--歌词滚动区 -->
      <div class="lyric-box">
        <div class="detail">
          <div class="wrapper">
            <ul class="lyric" v-show="isShowMusicList" ref="lyricRef">
              <li v-for="(item, index) of ParsedLyricsArray" :key="index">{{ item.c }}</li>
          </ul>
        </div>
      </div>
    </div >

对应歌曲滚动区的样式

.lyric-box {
  height: 40vh;
  overflow: hidden;
  position: relative;

  .not-data {
    margin-top: 1.875rem;
    font-size: 16px;
    color: #cdcdc6;
  }

  .detail {
    position: absolute;
    top: 0;
    bottom: 2.6rem;
    left: 0;
    right: 0;
    color: rgba(255, 255, 255, 0.65);

    .lineHigh {
      color: #FFD700;
    }

    .wrapper {
      overflow: hidden;
      position: absolute;
      top: 2rem;
      right: 0;
      left: 0;
      height: 545px;
      // background-color: red;

      ul {
        line-height: 32px;
        width: 100%;
        padding-bottom: 1rem;
        display: flex;
        flex-direction: column;
        flex-wrap: wrap;

        li {
          font-size: 16px;
          transition-duration: 1200ms;
        }
      }
    }
  }
}

定义的变量

   // 歌词节点
    const lyricRef = ref(null);
    const lineNo = ref(0);
    const Cpos = ref(7);
    const offset = ref(-32);

后台返的歌词数组 本来后端返回的是一个歌词URL地址的,点击地址链接可以直接拿到lrc文件。但是前端需要再请求一次歌词地址,结果产生了跨域问题。所以最后,还是请后端把数据处理好返回,这样子就省事很多了,方便后续直接做歌词的解析。

// 后台返的歌词数组 
lyricsArray: `
[ti:不一样了] 
[ar:许茹芸] 
[al:] 
[by:] 
[offset:0] 
[00:00.01]不一样了 - 许茹芸 
[00:01.24]词:天天 
[00:01.95]曲:马怡静 
[00:02.81] 
[00:19.21]打开窗帘的一刹那 
[00:22.73] 
[00:23.29]看窗户洒进来的阳光 
[00:27.02] 
[00:27.85]那天早晨的心情也完全不一样 
[00:34.59] 
[00:36.44]光线像双眼般透亮 
[00:39.76] 
[00:40.50]脸上也悄悄泛起红光 
[00:44.02] 
[00:44.76]莫名心动 
[00:46.52]因为你一一涌上 
[00:52.08] 
[00:53.33]在心的深处有一双翅膀 
`,

对歌词进行解析操作: 此处需要注意:后台返回的歌词数组格式不一定全部都是正确的,有的可能会缺失歌词时间之类的,所以可能会导致后面网页显示出现问题。所以,需要考虑做判断,在歌词返回格式不正确的时候,可以按无歌词状态来表示或者啥的......

// lyricsArray是后端获取到的lrc格式的歌词文件
if (singInfo.lyricsContent.length == 0) return;
let lrcs = singInfo.lyricsContent.split('\n');//用回车拆分成数组
// console.log(`歌词数据=======`, lrcs)
for (let i in lrcs) {//遍历歌词数组
  lrcs[i] = lrcs[i].replace(/(^\s*)|(\s*$)/g, ""); //去除前后空格
  // 歌词行数的播放时间
  let t = lrcs[i].substring(lrcs[i].indexOf("[") + 1, lrcs[i].indexOf("]"));//取[]间的内容
  let s = t.split(":");//分离:前后文字

  if (!isNaN(parseInt(s[0]))) { //是数值
    let arr = lrcs[i].match(/\[(\d+:.+?)\]/g);//提取时间字段,可能有多个
    let start = 0;
    for (let k in arr) {
      start += arr[k].length; //计算歌词位置
    }
    let content = lrcs[i].substring(start);//获取歌词内容
    for (let k in arr) {
      let t = arr[k].substring(1, arr[k].length - 1);//取[]间的内容
      let s = t.split(":");//分离:前后文字
      if (!content) {
        break;
      }
      ParsedLyricsArray.value.push({//对象{t:时间,c:歌词}加入ms数组
        t: (parseFloat(s[0]) * 60 + parseFloat(s[1])).toFixed(3),
        c: content
      });
    }
  }
}
 

解析好歌词后再进行歌词对应时间滚动

// 组件挂载时调用
onMounted(() => {
  // 监听歌词进度与播放高亮一致
  songPlayAudioRef.value.addEventListener('timeupdate', () => {
    if (lineNo.value == ParsedLyricsArray.value.length)
      return;
    // songPlayAudioRef.value.currentTime播放器时间
    if (parseFloat(ParsedLyricsArray.value[lineNo.value].t) <= songPlayAudioRef.value.currentTime) {
      lineHigh();//高亮当前行
      lineNo.value++;
    }
  })
})


  //结束执行
    const onended = () => {
      console.log('onended--结束执行-');
      goback(); //回滚歌词
    }

    // 重新播放是歌词重置事件
    const goback = () => {
      document.querySelector(".lineHigh").removeAttribute("class");
      lyricRef.value.style.transform = "translateY(0)";
      lineNo.value = 0; //lineNo清零,重新播放
    }

    // 高亮歌词滚动事件
    const lineHigh = () => {
      const list = lyricRef.value.getElementsByTagName("li");
      if (lineNo.value > 0) {
        list[lineNo.value - 1].removeAttribute("class");//去掉上一行的高亮样式
      }
      list[lineNo.value].className = "lineHigh";//高亮显示当前行

      //文字滚动
      if (lineNo.value > Cpos.value) {
        lyricRef.value.style.transform = "translateY(" + (lineNo.value - Cpos.value) * offset.value + "px)"; //整体向上滚动一行高
      }
    }

特别说明:每一篇文章,都是针对自己做过的项目而记下的笔记;笔记的内容也是参考网上的资料,我就是个搬运工,整合成了自己的笔记。如需要参考的,请根据自己实际项目需求参考。