Vue aduio播放歌曲

javascript 复制代码
<template >
   <div class="container">
      
      <div class="box1">
        <ul>
          <!-- <li v-for="(item,index) in lrc"  -->
            <li v-for="(item,index) in result" 
          :class="colorHover===index?'lrcHighLight':'lrcDefault'" 
          :key="index" @mousedown="mousedown(index,item)">
          {{ item.words }}
        </li>
      </ul>
    </div>

    <div class="box2">
        <audio 
              controls 
              ref="audio" 
              @timeupdate="timeUpdate"
              @ended="overAudio"
              @pause="onPause"
              @play="onPlay"
              >
        <source src="./song/song.mp3" type="audio/mpeg"/>
        </audio>
      </div>

   </div>
    
 
      
</template>
    
<script>
import  { lrc }  from  './song/index.js';

export default {
  name: "test",
  
  data() {
    return {
      result: [], // 歌词时间对象
      audio: this.$refs.audio,
      doms: {},
      colorHover: 0
    };
  },
  created() {
    // 解析歌词并渲染
    this.parseLrc();

  },

  computed: {
  
    // audioCurrentTime(){
    //   var _this = this;
    //   this.$nextTick(() => { 
    //     alert(_this.$refs.audio.currentTime);
    //     // return document.querySelector('audio').currentTime;
    //     return _this.$refs.audio.currentTime;
    //   })
      
    // }
  },
  watch: {
    // audioCurrentTime(oldTime, newTime) {
    
    //   console.log(1);
    //   this.findIndex();
    // }
  },
  mounted() {

    // this.addEventListeners();
    // this.$refs.audio.addEventListener('timeupdate', this.setOffset());
    this.doms = {
        audio: this.$refs.audio,
        ul: this.$el.querySelector(".container ul"),
        container: this.$el.querySelector('.container'),
      }

      // 播放器默认音量,最大音量1
      this.$refs.audio.volume = 0.25;
  },
  methods: {
    // addEventListeners: function () {
    //     this.$refs.audio.addEventListener('timeupdate', this.setOffset());
    //   },


    // addEventListeners: function () {
    //     const audio = this.$refs.audio;
    //     self.$refs.audio.addEventListener('timeupdate', audio.currentTime)
    //     // self.$refs.player.addEventListener('canplay', audio.durationTime)
    // },

    /** 解析歌词字符串
     * 得到一个歌词对象的数组
     * 每句歌词对象:
     * {time: 开始时间, words: 歌词}
     */
     parseLrc(){
      var lines = lrc.split("\n");
      // 存放所有时间/歌词数组
      this.result = [];
      lines.forEach(item => {
        var parts = item.split("]");
        var timeStr = parts[0].substring(1);

        // this.parseTime(timeStr);
        var obj = {
          time: this.parseTime(timeStr),
          words: parts[1]
        }
        this.result.push(obj);
      })
      // this.createLrcElement();
      return this.result;
    },

        // 原生js添加歌词
      createLrcElement(){
      // 文档片段
      var frag = document.createDocumentFragment();
      for(var i = 0; i<this.result.length; i++){
          var li = this.$el.createElement("li");
          li.textContent = this.result[i].words;
          frag.append(li);
      }
      this.doms.ul.appendChild(frag);
    },

    /** 
     * 将一个时间字符串解析数字(秒)
     * @param {string} timeStr 事件字符串
     * @return 
     */
     parseTime(timeStr){
      var parts =timeStr.split(":");
      return +parseInt(parts[0]) * 60 + parseInt(parts[1]);
    },

    /**
     * 计算出播放器当前播放时间应该高亮哪句歌词的下标
     * 
     */
    findIndex() {
      var lrcTime = this.result;
 
       // DOM上绑定的src属性无法立即更新
      
        // var curTime = document.querySelector('audio').currentTime;
        var curTime = this.$refs.audio.currentTime
        for(var i = 0; i < lrcTime.length; i++){
          // console.log('播放器当前时间', curTime)
          // console.log('歌词时间', lrcTime[i].time)

          if(curTime < lrcTime[i].time) {
            // console.log(lrcTime[i].words);
            return i - 1;
          }
        }
        // 如果遍历完没有歌词,说明播放到最后一句了
        return lrcTime.length - 1;
    

    },




    test(){
      // 容器高度
      var containerHeight = this.doms.container.clientHeight;
      // 每个 li 的高度
      var liHeight = this.doms.ul.children[0].clientHeight;

    },

    /**
     * 设置 Ul 的偏移量
     */
    setOffset(){
      
      // 容器高度
      // var containerHeight = this.doms.container.clientHeight;
      // // 每个 li 的高度
      // var liHeight = this.doms.ul.children[0].clientHeight;

      var index = this.findIndex();
      this.colorHover = index;
      // alert(index)
      // var offset = liHeight * index + liHeight / 2 - containerHeight / 2;
      // if(offset < 0){
      //   offset = 0;
      // }
      // if(offset > maxOffset){
      //   offset = maxOffset;
      // }

      // this.doms.ul.style.transform = `translateY(-${offset}px)`;
      // // 去掉之前的 active 的样式
      // var li = this.doms.ul.querySelector('.active');
      // if(li){
      //   li.classList.remove('active');
      // }
      //  li = this.doms.ul.children[index];
      // if(li){
      //   li.classList.add('active');
      // }
    },
    // 监听播放器时间
    timeUpdate() {
      this.setOffset();
    },
    // 当音频播放
    onPlay () {
        console.log('开始播放声音');
    },
    // 当音频暂停
    onPause () {
        console.log('暂停播放声音');
    },
    //播放完毕执行
    overAudio(){
        console.log('播放声音完毕');
    },
    mousedown(index, item){
      console.log(this.$refs.audio);
 
      this.$refs.audio.currentTime = item.time;
      this.timeUpdate();
    },

    

  }
};
</script>
<style lang="scss" scoped>
.lrcDefault{
 
  color:black;
  }
.lrcHighLight{  
  // transform:  scale(1.5);
  // transition: 1s ease-in; 
  color:#00a0e9;
  }
.box1{
  display: flex;
  justify-content: center;
  height: 90%;

 
}  
.box2{
  display: flex;
  justify-content: center;
  height: 10%;
  bottom: -12px;
  background-color: #000;
  box-shadow: 0 -10px 20px #403f3f;
}  
ul{
  list-style-type: none;
 
  height: 90%;
  overflow-y: scroll;

  scrollbar-color: dark;
  /** 隐藏滚动条,但是元素还是可以滚动 */
  scrollbar-width: none;
  li{
    height: 30px;
    line-height: 30px;
    font-size: 16px;
    cursor:pointer;
  }
}
  .container{
    height: 80vh;
    background: #708090;
}
 
  
</style>
      
      
java 复制代码
var lrc = `[0:00:00]小手拉大手-许梦
[0:06:00]词-陈大毛
[0:13:00]曲-过儿
[0:20:14]编曲-狗蛋
[0:27:08]还记得那场音乐会的烟火
[0:30:02]还记得那个凉凉的深秋
[0:34:13]还记得人潮把你推向了我
[0:38:13]游乐园拥挤的正是时候
[0:42:26]一个夜晚坚持不睡的等候
[0:45:01]一起泡温泉奢侈的享受
[0:49:01]有一次日记里愚蠢的困惑
[0:53:01]因为你的微笑幻化成风
[0:56:01]你大大的勇敢保护着我
[01:02:01]我小小的关怀喋喋不休
[01:04:01]感谢我们一起走了那么久
[01:07:01]又再一次回到凉凉深秋
[01:11:01]给我你的手
[01:13:34]像温柔野兽
[01:15:53]把自由交给草原的辽阔
[01:19:06]我们小手拉大手
[01:21:35]一起郊游
[01:23:11]今天别想太多
`;
// 注意"歌词行"别留空格
export {
    lrc,
  }

1.歌曲需下载

2.点击歌词播放,如果初始化时暂停状态,需手动点击播放(累,没去看aduio方法)

3.歌词时间不准确,自己手动输入的时间,只影响点击歌词播放功能一点点

4.本意是想用原生写(注释部分),但vue框架始终报错document未定义,未果。

5.写得有点乱,望海涵!

相关推荐
只会写Bug1 小时前
后台管理项目中关于新增、编辑弹框使用的另一种展示形式
前端·vue.js
M ? A2 小时前
Vue v-bind 转 React:VuReact 怎么处理?
前端·javascript·vue.js·经验分享·react.js·面试·vureact
军军君012 小时前
数字孪生监控大屏实战模板:政务服务大数据
前端·javascript·vue.js·typescript·前端框架·echarts·less
忆往wu前3 小时前
前端请求三部曲:Ajax / Fetch / Axios 演进与 Vue 工程化封装
前端·vue.js
.Cnn4 小时前
Ajax与Vue 生命周期核心笔记
前端·javascript·vue.js·笔记·ajax
吴声子夜歌4 小时前
Vue3——渲染函数
前端·vue.js·vue·es6
Ruihong5 小时前
你的 Vue KeepAlive 组件,VuReact 会编译成什么样的 React 代码?
vue.js·react.js·面试
Ruihong5 小时前
你的 Vue slot 插槽,VuReact 会编译成什么样的 React 代码?
vue.js·react.js·面试
一 乐5 小时前
房产租赁管理|基于springboot + vue房产租赁管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·房产租赁管理系统
2501_913680005 小时前
Vue3项目快速接入AI助手的终极方案 - 让你的应用智能升级
前端·vue.js·人工智能·ai·vue·开源软件