前端学习笔记|原生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
前端学习笔记|原生JS实现在线音乐播放器及歌词滚动
文章图片



    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 = https://www.it610.com/article/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 = 'https://www.it610.com/article/imgs/24gl-play.png' sta = 1 auo.pause() musboximg.style.animationPlayState = 'paused' } else { musstaimg.src = 'https://www.it610.com/article/imgs/24gl-pause2.png' sta = 0 auo.play() musboximg.style.animationPlayState = 'running' } } // 下一首事件函数 function naiods() { index++ if (index >= music.length) { index = 0 } auo.src = https://www.it610.com/article/music[index] getLRC(gcarray[index]) musboximg.src = musicimg[index] mustitle.innerHTML = `${musicname[index].split('-')[0]}` musnam.innerHTML = `${musicname[index].split('-')[1]}` musstaimg.src = 'https://www.it610.com/article/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 = https://www.it610.com/article/music[index] getLRC(gcarray[index]) musboximg.src = musicimg[index] mustitle.innerHTML = `${musicname[index].split('-')[0]}` musnam.innerHTML = `${musicname[index].split('-')[1]}` musstaimg.src = 'https://www.it610.com/article/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 = https://www.it610.com/article/(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 += '
  • ' + oLRC.ms[i].c + '
  • ' } 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 = https://www.it610.com/article/(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请求本地的歌词,如果各位有在线歌词地址可以直接替换。
    总结一下遇到的问题:
    前端学习笔记|原生JS实现在线音乐播放器及歌词滚动
    文章图片

    当我为滚动条添加进度跟随音乐播放时遇到的问题,直接用ontimeupdate事件会因为加载过快,在切换歌曲时还没加载到下一首歌曲的资源就直接执行了,因此会报错误,这里我们用定时器延迟他的执行速率,调整一下就可以了。
    【前端学习笔记|原生JS实现在线音乐播放器及歌词滚动】歌词滚动部分主要参考:歌词滚动部分参考链接

      推荐阅读