文章目录
  1. 1. 首先我们获取歌词:
  2. 2. 成功回调中使用 parseLyric方法解析歌词
  3. 3. 下一步,渲染歌词到界面上
  4. 4. 歌词滚动更新view
  5. 5. css 样式

在制作HTML5音乐播放器的时候,突然想到能不能像native的播放器一样做歌词滚动呢?lrc歌词实际上就是文本所以实现起来很容易。
实现方法,使用ajax加载lrc歌词-然后使用正则解析时间轴和歌词-渲染歌词-滚动效果

之前在百度音乐手机(web)版中发现他实现了该功能。于是自己在项目中也做了一个。于是自己在网上寻找了半天最终发现大多都是使用setInterval之类的来轮询滚动的,而且是用数组来存储的时间轴和歌词,实际上完全没必要这样浪费CPU。

我的实现方法是将时间轴和歌词都存储在hash表中,这样效率和速度都远超数组。js中object本身就是一个hash表。

首先我们获取歌词:

使用ajax读取lrc歌词文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    $.ajax({
url:this.url,
headers:{
contentType:"application/x-www-form-urlencoded"
},
success:function(lrc){
var lyric = parseLyric(lrc);
if(success)success(lyric);
},
error:function(e){
if(error)error(e);
}
});
};

读取的歌词请注意编码,如果和你网站的编码不符中文会显示乱码,建议去百度找歌词,百度的歌词都是UTF8编码的

成功回调中使用 parseLyric方法解析歌词

parseLyric解析歌词
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function parseLyric(lrc) {
var lyrics = lrc.split("\n");
var lrcObj = {};
for(var i=0;i<lyrics.length;i++){
var lyric = decodeURIComponent(lyrics[i]);
var timeReg = /\[\d*:\d*((\.|\:)\d*)*\]/g;
var timeRegExpArr = lyric.match(timeReg);
if(!timeRegExpArr)continue;
var clause = lyric.replace(timeReg,'');

for(var k = 0,h = timeRegExpArr.length;k < h;k++) {
var t = timeRegExpArr[k];
var min = Number(String(t.match(/\[\d*/i)).slice(1)),
sec = Number(String(t.match(/\:\d*/i)).slice(1));
var time = min * 60 + sec;
lrcObj[time] = clause;
}
}
return lrcObj;
}

使用换行符拆分歌词为数组,然后遍历每一行。
匹配时间轴匹配 使用正则/[\d:\d((.|\:)\d)]/g

lrc歌词的时间格式有很多种 比如 [00:02.69]沧浪之歌 [00:02:69]沧浪之歌 [00:02]沧浪之歌
这个正则可以匹配出所有格式,把歌词text放在 “clause”变量中
然后循环时间轴,使用正则找出时分秒,将它单位转换为 秒
最后用时间轴作为键,歌词作为值 放到lrcObj对象中。
使用对象来存储歌词在读取的时候比数组效率更好,而且还可以不用管2个时间轴一句歌词的情况,有的歌词是这样的,因为副歌部分很多重复内容,作者将他写在了同一行

1
[00:02:69][01:02:69] 哈哈

这样的格式使用数组存储的话在读取的时候就要做更多的处理了。

下一步,渲染歌词到界面上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    $player.bind("playing",function(){
renderLyric($player.music);
});

function renderLyric(music){
lyric.html("");
var lyricLineHeight = 27,
offset = lyric_wrap.offset().height*0.4;
music.lyric.fetch(function(data){
music.lyric.parsed = {};
var i = 0;
for(var k in data){
var txt = data[k];
if(!txt)txt = "&nbsp;";
music.lyric.parsed[k] = {
index:i++,
text:txt,
top: i*lyricLineHeight-offset
};
var li = $("<li>"+txt+"</li>");
lyric.append(li);
}
$player.bind("timeupdate",updateLyric);
},function(){
lyric.html("<li style='text-align: center'>歌词加载失败</li>");
});
}

监听音乐开始播放,开始播放的时候就去拉去对应的歌词并且渲染,在渲染的过程中我重新定义了一个歌词,新增了parsed属性,这个属性里面存储了每句歌词在hash表中的下标和top偏移量,我们需要使用这个偏移量来实现歌词滚动效果,在渲染的时候就计算出来,这样就不需要实时的去计算位置了。

歌词滚动更新view

歌词滚动代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$player.bind("timeupdate",updateLyric);
var text_temp;
function updateLyric(){
var data = $player.music.lyric.parsed;
if(!data)return;
var currentTime = Math.round(this.currentTime);
var lrc = data[currentTime];
if(!lrc)return;
var text = lrc.text;
if(text != text_temp){
locationLrc(lrc);
text_temp = text;
}
function locationLrc(lrc){
lyric_wrap.find(".lyric_wrap .on").removeClass("on");
var li = lyric_wrap.find("li:nth-child("+(lrc.index+1)+")");
li.addClass("on");
var top = Math.min(0,-lrc.top);
//lyric.css({"-webkit-transform":"translate(0,-"+lrc.top+"px)"});
lyric.css({"margin-top":top});
}
}

监听播放器的timeupdate事件,在回调中使用播放器当前时间轴位置在歌词对象中查找歌词。完全没必要使用interval来实现,因为timeupdate事件已经可以做到这个事情了!

text_temp 是记录上一句歌词,因为播放器时间轴一直在发生变化,但是歌词不一定更新了,所以判断了一下不再执行下面的代码了,没有此判断也不影响最终结果,只是最增加些许开销。

locationLrc:定位到当前歌词,对当前一局歌词增加class “on” 以在css中设置当前歌词样式,修改容器的偏移量,可以使用 transform和margin-top,还有top等多种方式实现,transform效率最高,但是在移动设备上会有一些莫名其妙的问题,所以我暂时使用的margin-top来代替,但是在移动设备上性能相对较差。

css 样式

歌词的样式表(LESS)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.panel.lyric{
color: #ffffff;
.lyric_wrap{
position: absolute;
bottom: 15px;
left: 0;
width: 100%;
top: 15px;
overflow: hidden;
#lyric{
padding: 5px;
-webkit-transition:500ms ease-out;
li{
text-align: center;
font-size: 15px;
padding: 5px 0;
&.on{
font-weight: bold;
color: #36a3e1;
}
}
}
}
}

这样一个动态滚动歌词的html5音乐播放器就搞定了!
以上代码来自项目:https://github.com/TivonJJ/html5-music-player
另外我还有一个仿MV的跑马灯字效果:点击查看

此文是本站原创,转载请标注作者和链接出处!