Vue implements lyrics scrolling

      I recently read a blog post about audio visualization, and I accidentally thought of the effect of lyric scrolling. Although there are many articles on the Internet that introduce lyric scrolling, not everyone has the same idea, so I would like to share with you some of my own ideas and how to achieve this function.

    Idea: Obtain the LRC lyrics data matched by the song  Netease Cloud Music LRC scrolling lyrics , obtain the real-time playback time of the audio, match the text corresponding to the time of the LRC lyrics, add animation to the current lyrics text through js control, and modify the animation execution time.

1. Processing lyrics data

    The original lyric data does not actually need us to generate, it is actually a string of strings with time and lyrics, first needs to be converted into an array object for easy use.

   Use Netease Cloud LRC to scroll through the lyrics to get the lyrics you want. For the specific method, please refer to the above link

First declare the formatLrc function to process the original data, intercept the string through split, remove the newline character, or get a new array, loop the new array, and intercept according to ']', the new array format is similar to ['[01:42.000','without you'] Obviously there are more '[' symbols, use the substring() method to intercept each '[', and save all the processed data, which is the data format we want. The specific code is as follows:

 formatLrc() {
      var strLrc = this.LRC.split("\n");//按换行截取字符串,去掉换行得到一个数组strLrc 
      let arr=[]//声明数组
      for (var i = 0; i < strLrc.length; i++) {//循环strLrc 
        var str = strLrc[i];
        var parts = str.split("]");//按照']'截取strLrc的每一项,得到新数组parts 
        var timeStr = parts[0].substring(1);//获取时间字符串
        var obj = {
          time: timeStr,
          words: parts[1],
        };
        arr.push(obj)//每循环一次把obj添加到arr数组末尾
      }
      return arr;
    },

  Through the above processing, a new data is obtained. The specific format of the data is as follows:

 2. Get the current playing time 

 <audio :src="music" @timeupdate="audioTime" controls></audio>

The audio playback control has an event @timeupdate event that can feed back the current playback progress information in real time and encapsulate the audioTime function. The specific code is as follows:

  audioTime(e) {
      var time = e.target.currentTime; //当前播放器时间
      for (var i = 0; i < this.lrcData.length; i++) {
        if (time < this.lrcData[i].time) {
          //循环歌词数组,当播放器当前时间第一次小于歌词时间时当前数组下标减一即为当前时间数组所对应歌词下标
          this.dataWords = this.lrcData[i - 1].words;
          //保存当前歌词动画执行事件
          this.lrcTime = this.lrcData[i].time - this.lrcData[i - 1].time;
          return i - 1;
        }
      }
    },

    After the current playback time is obtained, it matches the time in the lyrics array. When the current time of the player is less than the lyrics time for the first time, the subscript of the current array minus one is the subscript of the lyrics corresponding to the current time array.

The specific page layout I wrote here is relatively simple, mainly there is only one playback control

<template>
  <div>
    <audio :src="music" @timeupdate="audioTime" controls></audio>
    <div
      :class="className"
      :style="{ 'animation-duration': `${lrcTime}000ms` }" >
      {
   
   { dataWords }}
    </div>
  </div>
</template>

 CSS defines the animation to realize the slow change of lyrics color from left to right.

@keyframes scan {
  0% {
    background-size: 0 100%;
  }
  100% {
    background-size: 100% 100%;
  }
}
.text {
  background: #7e7e7e -webkit-linear-gradient(left, #76ca16, #0fa046) no-repeat 0
    0;
  -webkit-text-fill-color: transparent;
  -webkit-background-clip: text;
  background-size: 0 100%;
}
.load {
  background-size: 100% 100%;
  animation: scan linear;
}

    Since the singing time of each lyric is different, if the execution time of each lyric animation is the same, the page will be very stiff and awkward. I used a stupid method here, subtracting the time of the current lyrics from the time of the next lyric, and the result is the time required for singing the current lyric, and changing the animation time of the lyric to the currently acquired time, the effect of synchronizing the lyrics and singing time can be realized. The specific code is as follows:

 audioTime(e) {
      var time = e.target.currentTime; //当前播放器时间
      for (var i = 0; i < this.lrcData.length; i++) {
        if (time < this.lrcData[i].time) {
          //循环歌词数组,当播放器当前时间第一次小于歌词时间时当前数组下标减一即为当前时间数组所对应歌词下标
          this.lrcTime = this.lrcData[i].time - this.lrcData[i - 1].time;
          this.dataWords = this.lrcData[i - 1].words;
          return i - 1;
        }
      }
    },

To sum up, you can get a simple lyrics scroll with song synchronization effect!

Here is the full code:

<template>
  <div class="hello">
    <audio :src="music" @timeupdate="audioTime" controls></audio>
    <div
      :class="className"
      :style="{ 'animation-duration': `${lrcTime}000ms` }" >
      {
   
   { dataWords }}
    </div>
  </div>
</template>

<script>
import music from "../assets/music.mp3";
export default {
  data() {
    return {
      className: "text load",
      lrcTime: "",
      music,
      currenttime: "",
      LRC: `[00:00.000] 作词 : 周耀辉/李焯雄
[00:01.000] 作曲 : 林健华
[00:02.000] 编曲 : 林健华
[00:15.000]忽然之间
[00:18.000]天昏地暗
[00:21.000]世界可以忽然什么都没有
[00:28.000]我想起了你
[00:32.000]再想到自己
[00:35.000]我为什么总在非常脆弱的时候
[00:40.000]怀念你
[00:43.000]''
[00:44.000]我明白太放不开你的爱
[00:49.000]太熟悉你的关怀分不开
[00:54.000]想你算是安慰还是悲哀
[00:58.000]而现在就算时针都停摆
[01:03.000]就算生命像尘埃分不开
[01:08.000]我们也许反而更相信爱
[01:24.000]''
[01:25.000]如果这天地
[01:29.000]最终会消失
[01:32.000]不想一路走来珍惜的回忆
[01:38.000]没有你
[01:40.000]''
[01:41.000]我明白太放不开你的爱
[01:46.000]太熟悉你的关怀分不开
[01:51.000]想你算是安慰还是悲哀
[01:55.000]而现在就算时针都停摆
[02:00.000]就算生命像尘埃分不开
[02:05.000]我们也许反而更相信爱
[02:34.000]''
[02:35.000]我明白太放不开你的爱
[02:40.000]太熟悉你的关怀分不开
[02:45.000]想你算是安慰还是悲哀
[02:49.000]而现在就算时针都停摆
[02:54.000]就算生命像尘埃分不开
[03:00.000]我们也许反而更相信爱`,
      lrcData: "",
      dataWords: "",
    };
  },
  name: "HelloWorld",
  methods: {
    //歌词数据转化为数组
    formatLrc() {
      var strLrc = this.LRC.split("\n");
      let arr = [];
      for (var i = 0; i < strLrc.length; i++) {
        var str = strLrc[i];
        var parts = str.split("]");
        var timeStr = parts[0].substring(1);
        var obj = {
          time: this.formatTime(timeStr),
          words: parts[1],
        };
        arr.push(obj);
      }
      this.lrcData = arr;
    },
    //时间转换(秒)
    formatTime(time) {
      var parts = time.split(":"); //[03:00.000]==>[03,00.00]
      return +parts[0] * 60 + +parts[1]; //计算秒
    },
    audioTime(e) {
      var time = e.target.currentTime; //当前播放器时间
      for (var i = 0; i < this.lrcData.length; i++) {
        if (time < this.lrcData[i].time) {
          //循环歌词数组,当播放器当前时间第一次小于歌词时间时当前数组下标减一即为当前时间数组所对应歌词下标
          this.lrcTime = this.lrcData[i].time - this.lrcData[i - 1].time;
          this.dataWords = this.lrcData[i - 1].words;
          return i - 1;
        }
      }
    },
  },
  watch: {
    dataWords() {
      this.className = "text";
      setTimeout(() => {
        this.className = "text load";
      }, 50);
    },
  },
  mounted() {
    this.formatLrc();
  },
};
</script> 
<style scoped>
@keyframes scan {
  0% {
    background-size: 0 100%;
  }
  100% {
    background-size: 100% 100%;
  }
}
.text {
  background: #7e7e7e -webkit-linear-gradient(left, #76ca16, #0fa046) no-repeat 0
    0;
  -webkit-text-fill-color: transparent;
  -webkit-background-clip: text;
  background-size: 0 100%;
}
.load {
  background-size: 100% 100%;
  animation: scan linear;
}
</style>

      The code is very simple. The general idea and the code are here. You can refer to it. There are still many details that need to be optimized, such as animation switching, and the execution speed of each animation execution time. You can continue to optimize. Here is for your reference only! ! I also hope that you can give me more advice and leave a message.

Guess you like

Origin blog.csdn.net/2303_76218115/article/details/129657440