欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Vue2實(shí)現(xiàn)歌曲播放和歌詞滾動效果

 更新時間:2024年09月11日 11:06:05   作者:愉快的小跳蛙  
這篇文章主要介紹了如何基于Vue2實(shí)現(xiàn)歌曲播放和歌詞滾動效果,文中通過代碼示例和圖文講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴可以自己動手試一下

需求:需要實(shí)現(xiàn)歌詞滾動效果。

思路:通過js+css的transform屬性完成。

難點(diǎn):主要就是需要知道正在播放的歌詞是那句,然后對正在播放的歌詞進(jìn)行變色和放大,最難的就是讓高亮歌詞隨著歌曲播放滾動。

1.先看效果圖

2.處理歌詞格式(項(xiàng)目中有后端兄弟實(shí)現(xiàn)轉(zhuǎn)換的可以省略)

// 處理歌詞格式
parseLrc(musicLrc) {
  const lines = musicLrc?.split(`\n`);
  let lrcList = lines.map((line) => {
    let [time, words] = line?.split("]") ?? [null, null];
    return {
      time: this.parseTime(time.substring(1)) || null,
      words: words || null,
    };
  });
  let lrcListRes = lrcList.filter((v) => {
    return v.name !== null && v.words !== null;
  });
  // this.computingTime();
  return lrcListRes;
},
 
parseTime(t) {
  const part = t?.split(":");
  return Number(part[0] * 60) + Number(part[1]);
},

歌詞格式一般都是數(shù)組對象,對象的key各位可以自己根據(jù)需要命名,主要是思路。思路最重要!思路最重要!思路最重要!

我這里的數(shù)據(jù)格式如下圖

3.利用audio的timeupdate的事件來進(jìn)行計算歌詞是否高亮以及偏移量

// 添加audio事件
autoDown() {
  const that = this;
  var audio = document.getElementById("myAudio");
  audio.addEventListener("ended", function () {
    that.switchingBtn("down");
  });
  audio.addEventListener("timeupdate", function () {
    that.computingTime();
  });
},
 
// 計算播放時間對應(yīng)的下標(biāo)
computingTime() {
  let arr = this.selectedFiles[this.playIndex].lrcList || [];
  let currentTime = document.getElementById("myAudio")?.currentTime || 0;
  let index = arr.findIndex((e) => currentTime < e.time) - 1;
  this.currentIndex = index >= 0 ? index : arr.length - 1;
},
// 計算偏移量(保證高亮的歌詞在中間)
computingOffset(index) {
  // 外部大盒子高度
  let musicLrcBoxHeight = this.$refs.musicLrc?.clientHeight;
  // 歌詞總高度
  let musicLrcHeight = this.$refs.musicLrc_bady?.clientHeight;
  // 每個li高度
  let musicLrcLiHeight = this.$refs.musicLrc_item?.clientHeight || 22;
  // 歌詞偏移高度
  let offsetHenght =
    index * musicLrcLiHeight + musicLrcLiHeight / 2 - musicLrcBoxHeight / 2;
  // 最大偏移高度
  let offsetMax = musicLrcHeight - musicLrcBoxHeight + 10;
 
  if (offsetHenght < 0) {
    offsetHenght = 0;
  }
  // if (offsetHenght > offsetMax) {
  //   offsetHenght = offsetMax;
  // }
 
  this.$refs.musicLrc_bady.style.transform = `translateY(-${offsetHenght}px)`;
},

注意:這里的computingTime和computingOffset兩個事件是核心代碼?。?!

4.整個demo源碼

<template>
  <div class="h100 dis_sb bs">
    <div class="music bg-fff bs">
      <div class="p10 bs mb10" style="height: 40px">
        <el-button type="text" size="small" @click="triggerFileInput">
          選擇歌曲
        </el-button>
        <span class="f12 ml10">請先選擇本地音樂!??!</span>
        <input
          ref="audioInput"
          style="display: none; height: 10px"
          type="file"
          @change="handleFileSelect"
          multiple
          accept="audio/*"
        />
      </div>
 
      <div class="bs p10" v-if="selectedFiles?.length > 0" style="height: 60px">
        <audio
          id="myAudio"
          class="audio"
          controls
          :src="fileUrl || selectedFiles[0]?.url"
          autoplay
        />
        <div class="tac bs" style="line-height: 20px">
          <el-button type="text" size="small" @click="switchingBtn('up')">
            上一曲
          </el-button>
          <el-button type="text" size="small" @click="togglePlay">
            {{ playing ? "暫停" : "播放" }}
          </el-button>
          <el-button type="text" size="small" @click="switchingBtn('down')">
            下一曲
          </el-button>
        </div>
      </div>
 
      <div v-if="selectedFiles?.length > 0" class="p10 music_body">
        <ul class="main bs mt20">
          <li
            :class="[index == playIndex ? 'main_item_action li' : 'li']"
            v-for="(file, index) in selectedFiles"
            :key="index"
          >
            <p @click="choose(file, index)">{{ file.name }}</p>
          </li>
        </ul>
      </div>
    </div>
 
    <div class="bs h100 p10 musicLrc" ref="musicLrc">
      <ul class="musicLrc_bady tac f14" ref="musicLrc_bady">
        <li
          v-for="(v, i) in selectedFiles[playIndex]?.lrcList"
          :key="i"
          ref="musicLrc_item"
        >
          <p :class="[i == currentIndex ? 'musicLrc_action ' : '']">
            {{ v.words }}
          </p>
        </li>
        <div
          v-if="selectedFiles[playIndex]?.lrcList?.length == 0"
          class="tac bs"
          style="margin: auto; padding-top: 30px; color: #ccc"
        >
          暫無歌詞
        </div>
      </ul>
    </div>
  </div>
</template>
 
<script>
import musicList from "./musicList.js";
export default {
  data() {
    return {
      selectedFiles: [],
      lrcList: [],
      fileUrl: null,
      playing: false,
      isPlay: false,
      playIndex: 0,
      currentTime: 0,
      currentIndex: null,
    };
  },
 
  created() {
    musicList.forEach((e) => {
      if (e.lrc) {
        e.lrcList = this.parseLrc(e.lrc);
      } else {
        e.lrcList = [];
      }
    });
    this.selectedFiles = JSON.parse(JSON.stringify(musicList));
  },
  watch: {
    selectedFiles: {
      handler(newVal, oldVal) {
        if (newVal?.length > 4) {
          this.$nextTick(() => {
            this.fileUrl = newVal[0]?.url;
            this.playing = true;
            this.playIndex = 0;
          });
        }
      },
      deep: true,
    },
    $route: {
      handler(newVal, oldVal) {
        if (newVal?.path !== "/music") {
          this.$nextTick(() => {
            this.playing = false;
          });
        }
      },
      deep: true,
    },
    currentIndex: {
      handler(newVal, oldVal) {
        if (newVal > 0) {
          setTimeout(() => {
            this.computingOffset(newVal);
          }, 150);
        } else {
          this.computingOffset(0);
        }
      },
      deep: true,
    },
  },
 
  mounted() {
    this.$nextTick(() => {
      this.autoDown();
    });
  },
 
  methods: {
    // 添加本地音樂
    handleFileSelect(event) {
      const files = event.target.files;
      this.selectedFiles = JSON.parse(JSON.stringify(musicList));
      this.fileUrl = null;
      for (let i = 0; i < files?.length; i++) {
        const file = files[i];
        const reader = new FileReader();
        reader.onload = (e) => {
          const fileObj = {
            name: file.name,
            url: e.target.result,
            // lrc: null,
            lrcList: [],
          };
          this.selectedFiles.push(fileObj);
        };
        reader.readAsDataURL(file);
      }
    },
 
    triggerFileInput() {
      this.$refs.audioInput.click();
    },
    // 選擇播放歌曲
    choose(v, i) {
      const that = this;
      that.playing = true;
      that.fileUrl = that.selectedFiles[i]?.url;
      that.playIndex = i;
    },
    // 判斷播放狀態(tài)
    togglePlay() {
      const that = this;
      var audio = document.getElementById("myAudio");
      if (audio.paused) {
        audio.play();
        that.playing = true;
      } else {
        audio.pause();
        that.playing = false;
      }
    },
    // 添加audio事件
    autoDown() {
      const that = this;
      var audio = document.getElementById("myAudio");
      audio.addEventListener("ended", function () {
        that.switchingBtn("down");
      });
      audio.addEventListener("timeupdate", function () {
        that.computingTime();
      });
    },
    // 播放歌曲
    playAudio() {
      var audio = document.getElementById("myAudio");
      audio.play();
      this.currentIndex = 0;
    },
    // 切換歌曲
    switchingBtn(v) {
      const that = this;
      that.playing = true;
      const length = this.selectedFiles?.length || 0;
 
      if (v === "down") {
        this.playIndex = (this.playIndex + 1) % length;
      } else {
        this.playIndex = (this.playIndex - 1 + length) % length;
      }
      that.fileUrl = that.selectedFiles[that.playIndex]?.url;
 
      setTimeout(() => {
        that.playAudio();
      }, 150);
    },
    // 處理歌詞格式
    parseLrc(musicLrc) {
      const lines = musicLrc?.split(`\n`);
      let lrcList = lines.map((line) => {
        let [time, words] = line?.split("]") ?? [null, null];
        return {
          time: this.parseTime(time.substring(1)) || null,
          words: words || null,
        };
      });
      let lrcListRes = lrcList.filter((v) => {
        return v.name !== null && v.words !== null;
      });
      // this.computingTime();
      return lrcListRes;
    },
 
    parseTime(t) {
      const part = t?.split(":");
      return Number(part[0] * 60) + Number(part[1]);
    },
    // 計算播放時間對應(yīng)的下標(biāo)
    computingTime() {
      let arr = this.selectedFiles[this.playIndex].lrcList || [];
      let currentTime = document.getElementById("myAudio")?.currentTime || 0;
      let index = arr.findIndex((e) => currentTime < e.time) - 1;
      this.currentIndex = index >= 0 ? index : arr.length - 1;
    },
    // 計算偏移量(保證高亮的歌詞在中間)
    computingOffset(index) {
      // 外部大盒子高度
      let musicLrcBoxHeight = this.$refs.musicLrc?.clientHeight;
      // 歌詞總高度
      let musicLrcHeight = this.$refs.musicLrc_bady?.clientHeight;
      // 每個li高度
      let musicLrcLiHeight = this.$refs.musicLrc_item?.clientHeight || 22;
      // 歌詞偏移高度
      let offsetHenght =
        index * musicLrcLiHeight + musicLrcLiHeight / 2 - musicLrcBoxHeight / 2;
      // 最大偏移高度
      let offsetMax = musicLrcHeight - musicLrcBoxHeight + 10;
 
      if (offsetHenght < 0) {
        offsetHenght = 0;
      }
      // if (offsetHenght > offsetMax) {
      //   offsetHenght = offsetMax;
      // }
 
      this.$refs.musicLrc_bady.style.transform = `translateY(-${offsetHenght}px)`;
    },
  },
};
</script>
 
<style scoped>
.music {
  width: calc(50% - 5px);
  height: 100%;
  border-radius: 10px;
}
 
#myAudio {
  width: 100%;
  height: 30px;
}
 
.music_body {
  height: calc(100% - 126px);
  overflow-x: hidden;
  overflow-y: auto;
}
 
.main {
  .li {
    border: 1px solid #eee;
    border-radius: 5px;
    padding: 0 10px;
    margin-bottom: 10px;
    box-sizing: border-box;
    line-height: 30px;
    font-size: 12px;
    cursor: grab;
    color: #666;
  }
  .main_item_action {
    border: 1px solid #409eff;
    color: #409eff;
  }
}
 
.musicLrc {
  width: calc(50% - 5px);
  height: 100%;
  background-color: rgb(0, 0, 0);
  border-radius: 10px;
  color: #ccc;
  line-height: 22px;
  /* overflow: hidden; */
  transform: translateY();
  overflow-x: hidden;
  overflow-y: auto;
}
 
.musicLrc_bady {
  li {
    transition: 0.8s;
  }
}
 
.musicLrc_action {
  transform: scale(1.5);
  color: #409eff;
}
</style>

以上就是基于Vue2實(shí)現(xiàn)歌曲播放和歌詞滾動效果的詳細(xì)內(nèi)容,更多關(guān)于Vue2歌曲播放和歌詞滾動的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue 搭建后臺系統(tǒng)模塊化開發(fā)詳解

    vue 搭建后臺系統(tǒng)模塊化開發(fā)詳解

    這篇文章主要介紹了vue 搭建后臺系統(tǒng)模塊化開發(fā)詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-05-05
  • 解決vue.js在編寫過程中出現(xiàn)空格不規(guī)范報錯的問題

    解決vue.js在編寫過程中出現(xiàn)空格不規(guī)范報錯的問題

    下面小編就為大家?guī)硪黄鉀Qvue.js在編寫過程中出現(xiàn)空格不規(guī)范報錯的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • vue點(diǎn)擊當(dāng)前路由高亮小案例

    vue點(diǎn)擊當(dāng)前路由高亮小案例

    這篇文章主要為大家詳細(xì)介紹了vue點(diǎn)擊當(dāng)前路由高亮小案例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • vue中如何監(jiān)聽url地址欄參數(shù)變化

    vue中如何監(jiān)聽url地址欄參數(shù)變化

    這篇文章主要介紹了vue中如何監(jiān)聽url地址欄參數(shù)變化問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Vue2.0 實(shí)現(xiàn)移動端圖片上傳功能

    Vue2.0 實(shí)現(xiàn)移動端圖片上傳功能

    本文主要介紹VUE2.0圖片上傳功能的實(shí)現(xiàn)。原理是通過js控制和input標(biāo)簽的方式完成這一效果,無需加載其他組件。具體實(shí)例大家大家參考下本文
    2018-05-05
  • vue部署包可配置后臺接口地址的方法

    vue部署包可配置后臺接口地址的方法

    這篇文章主要介紹了vue部署包可配置后臺接口地址的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-03-03
  • 詳解vue的diff算法原理

    詳解vue的diff算法原理

    這篇文章主要介紹了詳解vue的diff算法原理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • vue實(shí)現(xiàn)html轉(zhuǎn)化pdf并復(fù)制文字

    vue實(shí)現(xiàn)html轉(zhuǎn)化pdf并復(fù)制文字

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)html轉(zhuǎn)化pdf的兩種方式,分別為能復(fù)制文字和不能復(fù)制文字的方法,有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-10-10
  • 在vue項(xiàng)目中正確使用iconfont的方法

    在vue項(xiàng)目中正確使用iconfont的方法

    今天小編就為大家分享一篇在vue項(xiàng)目中正確使用iconfont的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • VUE中對object.object和object[object]的使用解讀

    VUE中對object.object和object[object]的使用解讀

    這篇文章主要介紹了VUE中對object.object和object[object]的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評論