原生JS实现在线音乐播放器及歌词滚动

话不多说先上效果图,界面没怎么美化,音乐使用的在线链接,图片使用的也是在线链接,歌名和歌词使用的是本地信息
在这里插入图片描述
style

 * {
        margin: 0;
        padding: 0;
      }
      body {
        background-color: #2d2d2d;
      }
      #musbox {
        position: relative;
        margin: 80px auto;
        background-color: #fcfcfc;
        width: 300px;
        height: 500px;
        border-radius: 15px;
        text-align: center;
      }
      #musboximg {
        margin-top: 30px;
        margin-bottom: 30px;
        width: 180px;
        height: 180px;
        border-radius: 50%;
        animation: mymove 20s linear infinite;
      }
      #musnam {
        margin-top: 20px;
        margin-bottom: 10px;
      }
      button {
        width: 30px;
        height: 30px;
        border-radius: 50%;
        border: none;
        background-color: #fcfcfc;
        margin-top: 100px;
      }
      button:nth-of-type(2) {
        margin-left: 30px;
        margin-right: 30px;
      }
      button > img {
        width: 100%;
        height: 100%;
      }
      progress {
        width: 280px;
      }
      @keyframes mymove {
        0% {
          transform: rotate(0deg);
        }
        100% {
          transform: rotate(360deg);
        }
      }
      .lyric_area {
        position: absolute;
        bottom: 70px;
        /*歌词显示区域*/
        height: 80px; /*歌词区域高度*/
        overflow: hidden;
        margin-top: 15px;
      }
      #lyric {
        /*歌词列表*/
        line-height: 25px; /*行高,这个值要用在歌词滚动距离上*/
        transition-duration: 600ms; /*滚动速度*/
      }
      #lyric .lineHigh {
        /*高亮行*/
        color: red;
      }
      #lyric > li {
        width: 300px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      #fanhui {
        position: absolute;
        top: -100px;
        left: 0px;
        opacity: 0;
      }

HTML

 <div id="musbox">
      <audio src="" id="auo"></audio>
      <img src="" alt="" id="musboximg" />
      <h3 id="mustitle"></h3>
      <p id="musnam"></p>
      <progress max="100" value="0" id="progress"></progress>
      <div class="lyric_area">
        <ul id="lyric" type="none"></ul>
      </div>

      <button id="mustop"><img src="./imgs/上一首.png" alt="" /></button>
      <button id="mussta">
        <img id="musstaimg" src="./imgs/24gl-play.png" alt="" />
      </button>
      <button id="musbom"><img src="./imgs/下一首.png" alt="" /></button>
      <button id="fanhui"><img src="./imgs/返回.png" alt="" /></button>
    </div>

JS

    var progress = document.querySelector('progress')
    var index = 0 //当前播放的歌曲
    //歌曲链接存放地址
    var music = [
      'http://dl.stream.qqmusic.qq.com/C40000306XNI2518qE.m4a?guid=818258907&vkey=3E6B8303FBE637A22F898F3BD451925D312F8953C0418FFA404DCEFCAC06C193639380E063EE275E531D64281BDF20E4743ED960553DBFE2&uin=534808402&fromtag=120032',
      'http://dl.stream.qqmusic.qq.com/C400003jPrhs2i3glx.m4a?guid=4655831370&vkey=927F4B96A239B20152BCDCD3E09E265D409257926A77C472107F55C852D3669270EB39D500FE70D608771224340A4D321FF1FA69057FEDB8&uin=534808402&fromtag=120032',
      'http://dl.stream.qqmusic.qq.com/C400002fxyPd03wGST.m4a?guid=9182436588&vkey=74EEE70EA2F83B82CA928A746E8F0C849BF8C86783E9BD9FC9E317D7668410DF735ABD7C718D91BA675303CA67254FC792795E006EA60707&uin=534808402&fromtag=120032',
      'http://dl.stream.qqmusic.qq.com/RS02061DAjhF369Snq.mp3?guid=5910173232&vkey=01D0B205F29E49DDD0832971216E1486C23FE1B8E2A74F9E2DEFDBF835A86176EA1A2D7DE3FA1086F81FF0DA42B9A98371C7F163EF23DA71&uin=534808402&fromtag=120052',
      'http://dl.stream.qqmusic.qq.com/RS02060N8fi64ZBlE1.mp3?guid=7818777342&vkey=432502A6EE64A0330E9FC088A8DBAD57E4FB10DFE792BE102E94ECE4C3D0B60AAEBBCBFA5B99F4E404895258B6FCFC319FDEA062E6F054A8&uin=534808402&fromtag=120052',
      'http://dl.stream.qqmusic.qq.com/C40000182Rsa42ANA9.m4a?guid=6684174472&vkey=531E377E998E8A3716F2FDDFF62E6E61F8F4B2FB5F3F1BA56E92810838046281C74BFCA9EF3DDD6D937E0AC17707084B26028166F4C23F2B&uin=534808402&fromtag=120032',
      'http://dl.stream.qqmusic.qq.com/C400003hkB7p35njTP.m4a?guid=5241292852&vkey=57342D6181907B49C67101A7EEA12482E63663DA83D99315104B74A7F4023BA09EC9C8982A2DA7389304C6E51A7C622EF56421080F5E6063&uin=534808402&fromtag=120032',
    ]
    // 歌曲大图
    var musicimg = [
      'https://y.qq.com/music/photo_new/T002R300x300M000002HRHRB004tH9_1.jpg?max_age=2592000',
      'https://y.qq.com/music/photo_new/T002R300x300M0000039nM8m1yhEnK_1.jpg?max_age=2592000',
      'https://y.qq.com/music/photo_new/T002R300x300M000002VxplL2gXAuH_4.jpg?max_age=2592000',
      'https://y.qq.com/music/photo_new/T002R300x300M000002xoonH2Bk7FR_1.jpg?max_age=2592000',
      'https://y.qq.com/music/photo_new/T002R300x300M0000039nM8m1yhEnK_1.jpg?max_age=2592000',
      'https://y.qq.com/music/photo_new/T002R300x300M0000001n7a82gh6IY_1.jpg?max_age=2592000',
      'https://y.qq.com/music/photo_new/T002R300x300M000003Z53pQ3q9pEo_1.jpg?max_age=2592000',
    ]
    // 歌曲名称
    var musicname = [
      '平凡的一天 (Live) - 毛不易',
      '看得最远的地方 - 毛不易',
      '一荤一素 (Live) - 毛不易',
      '消愁 (Live) - 毛不易',
      '像我这样的人 (Live) - 毛不易',
      '光辉岁月 - BEYOND',
      '舞女 (国语)-韩宝仪.lrc',
    ]
    // 歌词名称
    var gcarray = [
      '01平凡的一天 (Live)-毛不易.lrc',
      '02看得最远的地方-毛不易.lrc',
      '03一荤一素 (Live)-毛不易.lrc',
      '04消愁 (Live)-毛不易.lrc',
      '05像我这样的人 -毛不易.lrc',
      '06光辉岁月-BEYOND.lrc',
      '07舞女 (国语)-韩宝仪.lrc',
    ]

    !(function () {
      auo.src = music[0]
      getLRC(gcarray[0])
      musboximg.src = musicimg[0]
      mustitle.innerHTML = `${musicname[0].split('-')[0]}`
      musnam.innerHTML = `${musicname[0].split('-')[1]}`
      musboximg.style.animationPlayState = 'paused'
    })()
    var sta = 1
    // 播放暂停事件
    mussta.onclick = function () {
      if (sta == 0) {
        musstaimg.src = './imgs/24gl-play.png'
        sta = 1
        auo.pause()
        musboximg.style.animationPlayState = 'paused'
      } else {
        musstaimg.src = './imgs/24gl-pause2.png'
        sta = 0
        auo.play()
        musboximg.style.animationPlayState = 'running'
      }
    }
    // 下一首事件函数
    function naiods() {
      index++
      if (index >= music.length) {
        index = 0
      }
      auo.src = music[index]
      getLRC(gcarray[index])
      musboximg.src = musicimg[index]
      mustitle.innerHTML = `${musicname[index].split('-')[0]}`
      musnam.innerHTML = `${musicname[index].split('-')[1]}`
      musstaimg.src = './imgs/24gl-pause2.png'
      auo.play()
      musboximg.style.animationPlayState = 'running'
    }
    // 下一首点击事件
    musbom.onclick = function () {
      naiods()
    }
    // 上一首点击事件
    mustop.onclick = function () {
      index--
      if (index < 0) {
        index = music.length - 1
      }
      auo.src = music[index]
      getLRC(gcarray[index])
      musboximg.src = musicimg[index]
      mustitle.innerHTML = `${musicname[index].split('-')[0]}`
      musnam.innerHTML = `${musicname[index].split('-')[1]}`
      musstaimg.src = './imgs/24gl-pause2.png'

      auo.play()
      musboximg.style.animationPlayState = 'running'
    }
    // 自动下一首
    auo.onended = function () {
      naiods()
    }

    // 滚动条事件 已添加到JS文件中
    // auo.shouldUpdate = true
    // auo.ontimeupdate = function () {
    //   var _this = this
    //   if (_this.shouldUpdate) {
    //     _this.shouldUpdate = false
    //     setTimeout(function () {
    //       console.log((auo.currentTime / auo.duration) * 100)
    //       progress.value = (auo.currentTime / auo.duration) * 100
    //       _this.shouldUpdate = true
    //     }, 800)
    //   }
    // }
    // 滚动条点击事件
    progress.onclick = function () {
      var x = event.offsetX
      var w = progress.offsetWidth
      auo.currentTime = (x / w) * auo.duration
    }
    // 点击头像切换歌词显示
    var lyric_area = document.getElementsByClassName('lyric_area')[0]
    musboximg.onclick = function () {
      fanhui.style.opacity = 1
      musboximg.style.opacity = '0'
      mustitle.style.opacity = '0'
      musnam.style.opacity = '0'
      mustop.style.opacity = '0'
      mussta.style.opacity = '0'
      musbom.style.opacity = '0'
      progress.style.opacity = '0'
      lyric_area.style.top = '10px'
      lyric_area.style.height = '450px'
    }
    fanhui.onclick = function () {

由于JS代码太多太乱了,我直接把他封装到另一个JS文件中了,直接导入即可

var lrc = ''
function getLRC(gct) {
  var ajax = new XMLHttpRequest()
  ajax.open('GET', gct)
  ajax.onreadystatechange = function () {
    if (ajax.readyState == 4 && ajax.status == 200) {
      lrc = ajax.responseText
      // console.log(lrc)
      var oLRC = {
        ti: '', //歌曲名
        ar: '', //演唱者
        al: '', //专辑名
        by: '', //歌词制作人
        offset: 0, //时间补偿值,单位毫秒,用于调整歌词整体位置
        ms: [], //歌词数组{t:时间,c:歌词}
      }
      function createLrcObj(lrc) {
        if (lrc.length == 0) return
        var lrcs = lrc.split('\n') //用回车拆分成数组
        for (var i in lrcs) {
          //遍历歌词数组
          lrcs[i] = lrcs[i].replace(/(^\s*)|(\s*$)/g, '') //去除前后空格
          var t = lrcs[i].substring(
            lrcs[i].indexOf('[') + 1,
            lrcs[i].indexOf(']')
          ) //取[]间的内容
          var s = t.split(':') //分离:前后文字
          if (isNaN(parseInt(s[0]))) {
            //不是数值
            for (var i in oLRC) {
              if (i != 'ms' && i == s[0].toLowerCase()) {
                oLRC[i] = s[1]
              }
            }
          } else {
            //是数值
            var arr = lrcs[i].match(/\[(\d+:.+?)\]/g) //提取时间字段,可能有多个
            var start = 0
            for (var k in arr) {
              start += arr[k].length //计算歌词位置
            }
            var content = lrcs[i].substring(start) //获取歌词内容
            if (content.length > 0) {
              for (var k in arr) {
                var t = arr[k].substring(1, arr[k].length - 1) //取[]间的内容
                var s = t.split(':') //分离:前后文字
                oLRC.ms.push({
                  //对象{t:时间,c:歌词}加入ms数组
                  t: (parseFloat(s[0]) * 60 + parseFloat(s[1])).toFixed(3),
                  c: content,
                })
              }
            }
          }
        }
        oLRC.ms.sort(function (a, b) {
          //按时间顺序排序
          return a.t - b.t
        })
        function showLRC() {
          lyric.innerHTML = ``
          var s = ''
          for (var i in oLRC.ms) {
            //遍历ms数组,把歌词加入列表
            s += '<li>' + oLRC.ms[i].c + '</li>'
          }
          document.getElementById('lyric').innerHTML = s
        }
        showLRC()
      }
      createLrcObj(lrc)
      var lineNo = 0 //当前行
      var C_pos = 1 //C位
      var offset = -25 //滚动距离(应等于行高)
      var audio = document.getElementById('auo') //播放器
      var ul = document.getElementById('lyric') //歌词容器列表
      //高亮显示歌词当前行及文字滚动控制,行号为lineNo
      function lineHigh() {
        var lis = document.getElementsByTagName('li') //歌词数组
        if (lineNo > 0) {
          lis[lineNo - 1].removeAttribute('class') //去掉上一行的高亮样式
        }
        lis[lineNo].className = 'lineHigh' //高亮显示当前行
        //文字滚动
        if (lineNo > C_pos) {
          ul.style.transform = 'translateY(' + (lineNo - C_pos) * offset + 'px)' //整体向上滚动一行高度
        }
      }
      //取消之前所有高亮
      function lineHigh2() {
        var lis = ul.getElementsByTagName('li') //歌词数组
        for (var i = 0; i < lis.length; i++) {
          var name = lis[i].className
          if (name != null) lis[i].removeAttribute('class') //去掉高亮样式
        }
      }

      //跳跃播放时,歌词回滚到对应位置
      audio.onseeked = function () {
        var curTime = audio.currentTime //播放器时间
        for (i = 0; i < oLRC.ms.length; i++) {
          if (oLRC.ms[i].t <= curTime) {
          } else {
            lineHigh2() //取消之前所有高亮
            lineNo = i
            lineHigh() //高亮当前行
            break
          }
        }
      }
      //滚回到开头,用于播放结束时
      function goback() {
        document.querySelector('#lyric .lineHigh').removeAttribute('class')
        ul.style.transform = 'translateY(0)'
        lineNo = 0
      }
      //监听播放器的timeupdate事件,实现文字与音频播放同步
      auo.shouldUpdate = true
      audio.ontimeupdate = function () {
        var _this = this
        if (_this.shouldUpdate) {
          _this.shouldUpdate = false
          setTimeout(function () {
            console.log((auo.currentTime / auo.duration) * 100)
            progress.value = (auo.currentTime / auo.duration) * 100
            _this.shouldUpdate = true
          }, 800)
        }
        if (lineNo == oLRC.ms.length) return
        var curTime = audio.currentTime //播放器时间
        if (parseFloat(oLRC.ms[lineNo].t) <= curTime) {
          lineHigh() //高亮当前行
          lineNo++
        }
      }

      //监听播放器的ended事件,播放结束时回滚歌词
      // audio.onended = function () {
      //   goback() //回滚歌词
      // }
    }
  }
  ajax.send(null)
}

对了提醒一下一定要用Live Server打开,因为用了ajax请求本地的歌词,如果各位有在线歌词地址可以直接替换。
总结一下遇到的问题:
在这里插入图片描述
当我为滚动条添加进度跟随音乐播放时遇到的问题,直接用ontimeupdate事件会因为加载过快,在切换歌曲时还没加载到下一首歌曲的资源就直接执行了,因此会报错误,这里我们用定时器延迟他的执行速率,调整一下就可以了。

歌词滚动部分主要参考:歌词滚动部分参考链接

猜你喜欢

转载自blog.csdn.net/weixin_46073653/article/details/125418341
今日推荐