js解析lrc 实现lrc歌词同步滚动效果

这两天想着做一个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>
可能会有点不足之处,还望大佬们指正

 

 

  • 25
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值