这两天想着做一个h5的网页播放器,实现歌词同步滚动
但是上网找了很多资料,竟发现没有比较完善的代码供参考,但是无意间看到了百度的千千音乐有这样的效果,就想着一定能通过js实现
经过两三天的努力,终于做出了这样的效果
实现功能
- 歌词同步滚动
- 同步歌词高亮效果
代码实现
首先,在body中加audio 和 放歌词的ul标签
<div>
<ul id="lrclist" style="transform: translateY(250px);"><!-- 保证歌词在正中间 -->
</ul>
</div>
<audio id="audio" src="audio/1.mp3" controls="controls" autoplay="true"></audio>
然后就是读取lrc文件,在进行解析,解析出歌词及对应的时间
一般的思路是这样的:通过Ajax或其他方式读取lrc文件
但我的专业是后端开发,js,啥的,都是自学的,于是遇到了不少坑,,在我千方百计想获取lrc文件时,总是报错,,只能通过http,data 协议获取文件,加上我用的是notepad开发的,调试了亿遍都失败
最后只好放弃了,我把lrc数据直接定义在js中,最后实现了同样的效果,
var lrcJSON = {
"[00:16.810]":"誰にも言えない孤独を抱えたまま1人で泣いてた",
"[00:28.040]":"誰にも上手に笑えない僕ら 2人出会って笑った",
"[00:39.710]":"",
"[00:40.410]":"真夜中過ぎに 想像の奥に",
"[00:46.150]":"どうしようもない気持ちをただ押しやって",
"[00:50.780]":"",
"[00:51.480]":"サヨナラ 僕らはきっと生まれ変われるから",
"[00:57.030]":"悲しみに手を振るんでしょう",
"[01:01.190]":"そしていつか 空っぽのままの心に灯りをともすように",
"[01:08.630]":"とぎれとぎれの 言葉を探して繋ぎ止めた",
"[01:14.390]":"それでもたった二人の世界は続いて行くんだ",
"[01:20.190]":"空に祈るように どうか永遠に",
"[01:29.380]":"",
"[01:43.670]":"白・黒・壱・零 僕らは選ぶ事を迫られてばかり",
"[01:54.940]":"そうして残った大事な物を壊れるほど抱きしめていた",
"[02:06.670]":"",
"[02:07.670]":"明け方過ぎの光の先に",
"[02:13.530]":"ほんの少しだけ未来が見えた気がして",
"[02:18.110]":"",
"[02:18.810]":"バカげた夢だってきっと願い続けるから",
"[02:24.330]":"奇跡に近づくんでしょう",
"[02:28.590]":"それは君の弱さの隣に見つけた強さの欠片だって",
"[02:36.160]":"とぎれとぎれの 言葉を探して繋ぎ止めた",
"[02:41.700]":"瞼の奥に滲んだ世界を守ってゆくんだ",
"[02:47.470]":"空に祈るように 君と誓う明日",
"[02:58.720]":"",
"[03:08.900]":"不自由な思いが重なり合ったら",
"[03:14.390]":"そうさ誰より自由になれた",
"[03:20.390]":"足りないものを埋め合うように",
"[03:25.700]":"疑いもせず 支えあいながら",
"[03:31.610]":"",
"[03:34.060]":"サヨナラ 僕らはきっと生まれ変われるから",
"[03:39.490]":"悲しみに手を振るんでしょう",
"[03:44.450]":"そしていつか 空っぽのままの心に灯りをともすように",
"[03:51.620]":"とぎれとぎれの 言葉を探して繋ぎ止めた",
"[03:57.190]":"それでもたった二人の世界は続いて行くんだ",
"[04:03.000]":"空に祈るように どうか永遠に…"
};
(有需要的可以仿照网上的Ajax获取lrc文件,再进行解析)
(不过lrc歌词文件可没有那么简单,开头可能还有歌名,歌手等信息,后边可能还有歌词对应的翻译。这里我是简化了数据。)
然后获取lrc数据,在ul中填充歌词,同时获取歌词对应的时间数据
var lrcTime = [];//歌词对应的时间数组
var ul = $("#lrclist")[0];//获取ul
var i = 0;
$.each(lrcJSON, function(key, value) {//遍历lrc
lrcTime[i++] = parseFloat(key.substr(1,3)) * 60 + parseFloat(key.substring(4,10));//00:00.000转化为00.000格式
ul.innerHTML += "<li><p>"+lrcJSON[key]+"</p></li>";//ul里填充歌词
});
接下来是播放时的自动滚动,用到了audio的timeupdate事件
audio.ontimeupdate = function() {//audio时间改变事件
currentTime = audio.currentTime;
for (j=currentLine, len=lrcTime.length; j<len; j++){
if (currentTime<lrcTime[j+1] && currentTime>lrcTime[j]){
currentLine = j;
ppxx = 250-(currentLine*32);
ul.style.transform = "translateY("+ppxx+"px)";
$li.get(currentLine-1).className="";
console.log("on"+currentLine);
$li.get(currentLine).className="on";
break;
}
}
};
最后是调整播放位置时触发的audio的seeked事件
audio.onseeked = function() {//audio进度更改后事件
currentTime = audio.currentTime;
console.log(" off"+currentLine);
$li.get(currentLine).className="";
for (k=0, len=lrcTime.length; k<len; k++){
if (currentTime<lrcTime[k+1] && currentTime<lrcTime[k]){
currentLine = k;
break;
}
}
};
实现效果
最后附上完整代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
<title>LRC</title>
<script src="js/jquery.js"></script>
<style>
div{
width:340px;
height:500px;
margin:0 auto;
overflow:hidden;
}
ul{
transition-duration: 600ms;
}
ul, li{
list-style:none;
padding: 0;
margin: 0;
}
li.on{
color: red;
}
p{
overflow: hidden;
text-overflow: ellipsis;
height: 16px;
line-height: 16px;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
display: -webkit-box;
text-align: center;
margin-top: 0;
margin-bottom: 16px;
}
audio{
margin-top: 10px;
}
</style>
</head>
<body>
<div>
<ul id="lrclist" style="transform: translateY(250px);"><!-- 保证歌词在正中间 -->
</ul>
</div>
<audio id="audio" src="audio/1.mp3" controls="controls" autoplay="true"></audio>
<script>
var lrcJSON = {
"[00:16.810]":"誰にも言えない孤独を抱えたまま1人で泣いてた",
"[00:28.040]":"誰にも上手に笑えない僕ら 2人出会って笑った",
"[00:39.710]":"",
"[00:40.410]":"真夜中過ぎに 想像の奥に",
"[00:46.150]":"どうしようもない気持ちをただ押しやって",
"[00:50.780]":"",
"[00:51.480]":"サヨナラ 僕らはきっと生まれ変われるから",
"[00:57.030]":"悲しみに手を振るんでしょう",
"[01:01.190]":"そしていつか 空っぽのままの心に灯りをともすように",
"[01:08.630]":"とぎれとぎれの 言葉を探して繋ぎ止めた",
"[01:14.390]":"それでもたった二人の世界は続いて行くんだ",
"[01:20.190]":"空に祈るように どうか永遠に",
"[01:29.380]":"",
"[01:43.670]":"白・黒・壱・零 僕らは選ぶ事を迫られてばかり",
"[01:54.940]":"そうして残った大事な物を壊れるほど抱きしめていた",
"[02:06.670]":"",
"[02:07.670]":"明け方過ぎの光の先に",
"[02:13.530]":"ほんの少しだけ未来が見えた気がして",
"[02:18.110]":"",
"[02:18.810]":"バカげた夢だってきっと願い続けるから",
"[02:24.330]":"奇跡に近づくんでしょう",
"[02:28.590]":"それは君の弱さの隣に見つけた強さの欠片だって",
"[02:36.160]":"とぎれとぎれの 言葉を探して繋ぎ止めた",
"[02:41.700]":"瞼の奥に滲んだ世界を守ってゆくんだ",
"[02:47.470]":"空に祈るように 君と誓う明日",
"[02:58.720]":"",
"[03:08.900]":"不自由な思いが重なり合ったら",
"[03:14.390]":"そうさ誰より自由になれた",
"[03:20.390]":"足りないものを埋め合うように",
"[03:25.700]":"疑いもせず 支えあいながら",
"[03:31.610]":"",
"[03:34.060]":"サヨナラ 僕らはきっと生まれ変われるから",
"[03:39.490]":"悲しみに手を振るんでしょう",
"[03:44.450]":"そしていつか 空っぽのままの心に灯りをともすように",
"[03:51.620]":"とぎれとぎれの 言葉を探して繋ぎ止めた",
"[03:57.190]":"それでもたった二人の世界は続いて行くんだ",
"[04:03.000]":"空に祈るように どうか永遠に…"
};
var lrcTime = [];//歌词对应的时间数组
var ul = $("#lrclist")[0];//获取ul
var i = 0;
$.each(lrcJSON, function(key, value) {//遍历lrc
lrcTime[i++] = parseFloat(key.substr(1,3)) * 60 + parseFloat(key.substring(4,10));//00:00.000转化为00.000格式
ul.innerHTML += "<li><p>"+lrcJSON[key]+"</p></li>";//ul里填充歌词
});
lrcTime[lrcTime.length] = lrcTime[lrcTime.length-1] + 3;//如不另加一个结束时间,到最后歌词滚动不到最后一句
var $li = $("#lrclist>li");//获取所有li
var currentLine = 0;//当前播放到哪一句了
var currentTime;//当前播放的时间
var audio = document.getElementById("audio");
var ppxx;//保存ul的translateY值
audio.ontimeupdate = function() {//audio时间改变事件
currentTime = audio.currentTime;
for (j=currentLine, len=lrcTime.length; j<len; j++){
if (currentTime<lrcTime[j+1] && currentTime>lrcTime[j]){
currentLine = j;
ppxx = 250-(currentLine*32);
ul.style.transform = "translateY("+ppxx+"px)";
$li.get(currentLine-1).className="";
console.log("on"+currentLine);
$li.get(currentLine).className="on";
break;
}
}
};
audio.onseeked = function() {//audio进度更改后事件
currentTime = audio.currentTime;
console.log(" off"+currentLine);
$li.get(currentLine).className="";
for (k=0, len=lrcTime.length; k<len; k++){
if (currentTime<lrcTime[k+1] && currentTime<lrcTime[k]){
currentLine = k;
break;
}
}
};
</script>
</body>
</html>
![](https://img-blog.csdnimg.cn/20181126204442688.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l6eV9jc2Ru,size_16,color_FFFFFF,t_70)