效果图

在这里插入图片描述

注:歌词和图片来源于网络

总体思路

①:请求歌词资源
②:解析歌词,生成html代码
③:html代码插入歌词显示的区域
④:监听歌曲播放进度,通过id选择器选中正在播放的歌词添加样式
⑤:判断条件是否滚动
⑥:歌曲播放完毕清除样式,滚动到顶部

①:请求歌词资源

 init() {
		//请求资源
      this.axios.get("/dataJson/musicList.json").then(response => {
        let { data, status } = response;//对象解析
        if (status != 200) return Error("资源请求出错");
		if(data.list[0].lyc!=undefined)
		{
			//请求歌词
			this.axios.get(data.list[0].lyc).then(response=>{
				//vuex全局状态管理,触发changeInfo事件
				this.$store.commit("changeInfo", {
				  name: data.list[0].name,
				  singer: data.list[0].singer,
				  lyc:response.data
				});
			})
		}
      });

json文件格式

{
	"list":[
		{
			"name":"他只是经过",
			"singer":"h3R3",
			"compose":"h3R3",
			"src":"./music/test1.mp3",
			"lyc":"./music/test1.mp3"
		}
	]
}

vuex中state数据和mutations中的方法

 state: {
    title: "歌名",
    msg: "歌曲附加信息",
    lyc: "",
    lycArr: []
  },
   mutations: {
    //只有在mutations中才能修改state状态
    changeInfo(state, newValue) {
      //修改信息
      state.title = newValue.name;
      state.msg = newValue.singer;
      state.lyc = newValue.lyc;
    }
  }

②:解析歌词,生成html代码

解析歌词在vuex的mutations中

initialize(state, lyc) {
      //解析歌词
      let lycArr = state.lyc.split("\n"); //拆分为数组
      for (let item of lycArr) {
        let flag = false;
        let arr = item.match(/\[(\d+:.+?)\]/g); //提取时间字段,可能有多个
        let start = 0;
        for (let k of arr) {
          start += k.length; //计算歌词位置(用于下一步提取歌词)
        }
        let content = item.substring(start); //提取从start提取文字一直到最后
        for (let value of arr) {
          let t = value.substring(1, value.length - 1); //取[]间的内容
          let s = t.split(":"); //分离:前后文字
          let time = (parseFloat(s[0]) * 60 + parseFloat(s[1])).toFixed(0);
          for (let t of state.lycArr) {
            if (time == t.time) {//防止时间重复
              flag = true;
              t.c += ` ${content}`;
              break;
            }
          }
          if (!flag) {//如果时间不重复
            state.lycArr.push({
              //对象{t:时间,c:歌词}加入ms数组
              time: time,
              c: content
            });
          }
        }
      }
      //生成html
      let html = `<p>${state.title}</p>`;
      for (let item of state.lycArr) {
        html += `<p id=${item.time}>${item.c}</p>`;
      }
      //插入展示区
      lyc.lycContainer.innerHTML = html;
    }

state.lycArr数组生成的结果如下
在这里插入图片描述

③:html代码插入歌词显示的区域

第二步mutations中,这行代码就是把html代码插入到歌词显示区

     //插入展示区
      lyc.lycContainer.innerHTML = html;

现在数据已经有了,来看一下显示区样式

<template>
  <div v-bind:class="{ lycContainer: true }"></div>
</template>

<style lang="less">
.lycContainer {
  height: 100%;
  padding: 0.625rem;
  box-sizing: border-box;
  font-size: 0.55rem;
  text-align: center;
  overflow-x: hidden;
  overflow-y: auto;
  scrollbar-width: none;//兼容火狐浏览器,隐藏滚动条
}
.lycContainer::-webkit-scrollbar {//兼容webkit内核浏览器,隐藏滚动条
  display: none;
}
</style>

④:监听歌曲播放进度,通过id选择器选中正在播放的歌词添加样式

当点击开始播放按钮时,进行歌曲播放进度监听

start() {
      if (!this.startFlag) {
        this.$refs.startClick.src = "./publicImg/pause.png";//图片,立即替换为暂停按钮
        this.startFlag = true;
        sound.title = "暂停";
        this.lycContainerHeight = document
          .getElementsByClassName("lycContainer")[0]
          .getBoundingClientRect()
          .height.toFixed(0);
        sound.addEventListener("timeupdate", this.lycMonitor, false);// 在音频/视频(audio/video)的播放位置发生改变时触发。
        sound.addEventListener("ended", this.musicEnd, false);//在音频/视频(audio/video)播放结束触发
        sound.play();
      } else {
        this.$refs.startClick.src = "./publicImg/start.png";
        this.startFlag = false;
        sound.title = "播放";
        sound.pause();
      }

歌词滚动。通过下面lycMonitor函数进行歌词滚动

lycMonitor() {
      //歌词监听滚动
      let lycId = document.getElementById(sound.currentTime.toFixed(0));
      if (lycId) {
        if (
          lycId.offsetTop >
          (
            this.lycContainerHeight / 2 +
            lycId.getBoundingClientRect().height
          ).toFixed(0)
        ) {
          document.getElementsByClassName("lycContainer")[0].scrollTop = (
            lycId.offsetTop -
            this.lycContainerHeight / 2
          ).toFixed(0);
        }
        lycId.previousSibling.removeAttribute("style");//因为每个id,都不一样。当前lycid播放时,移出上一个歌词样式
        lycId.style.cssText =
          "background: linear-gradient(-3deg,rgba(184,134,11,0.9) 0%,rgba(255,255,0,0.6) 60%);-webkit-background-clip: text;color: transparent;transform: scale(1.2);transition: all .5s ease-in;";//添加歌词样式
      }
    },

⑤:判断条件是否滚动

判断滚动条件如下

if (
          lycId.offsetTop >
          (
            this.lycContainerHeight / 2 +
            lycId.getBoundingClientRect().height
          ).toFixed(0)
        ) {
          document.getElementsByClassName("lycContainer")[0].scrollTop = (
            lycId.offsetTop -
            this.lycContainerHeight / 2
          ).toFixed(0);
        }

当当前歌词距离页面顶部距离大于歌词显示区域一半加上当前歌词高度时,开始滚动

⑥:歌曲播放完毕清除样式,滚动到顶部

musicEnd() {
      document
        .getElementsByClassName("lycContainer")[0]
        .lastChild.removeAttribute("style"); //删除最后一个p的style
      let timer = setInterval(() => {
        //匀速回到开头
        document.getElementsByClassName("lycContainer")[0].scrollTop -= 10;
        if (document.getElementsByClassName("lycContainer")[0].scrollTop == 0) {
          clearInterval(timer);
        }
      }, 10);
    }
Logo

前往低代码交流专区

更多推荐