话不多说先上效果图,界面没怎么美化,音乐使用的在线链接,图片使用的也是在线链接,歌名和歌词使用的是本地信息
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事件会因为加载过快,在切换歌曲时还没加载到下一首歌曲的资源就直接执行了,因此会报错误,这里我们用定时器延迟他的执行速率,调整一下就可以了。
歌词滚动部分主要参考:歌词滚动部分参考链接