放一个歌词同步的JS UI
很久没发文,一直想写点东西,整理整理这阵子的心得,很多笔记都在整理中。
最近给公司编写的一个JS UI,用于歌词同步,整理一下放出来,
核心脚本只负责处理lrc格式的歌词和呈现,并提供同步功能。
外部呈现等均可以良好定制。
基本调用如下:
var lrc=new LRC({lyricTable:obj,lyricWrapper:obj,curRowClassName:'xx',lyric:'xxx',separator:'
'});
if(lrc.IsLyricValid()) lrc.DoSync(60);
DoSync(t)用于同步,参数t为当前播放进度,从播放器获得。
IsLyricValid()返回歌词是否合法的LRC格式。
贴出代码,附件下载中包含2个定制示例。代码在FF下跑不了,因为音乐播放插件跑不了~
Lrc UI
1/**//*
2* @author: huangxu
3* @date: 2008-11
4* @site: http://wsky.cnblogs.com
5* @descript: sync display the lyric
6* @usage:
7* //import lrc.css
8* var lrc=new LRC({lyricTable:obj,lyricWrapper:obj,curRowClassName:'xx',lyric:'xxx',separator:'
'});
9* if(lrc.IsLyricValid()) lrc.DoSync(60);
10*
11* @note: 内部变量前缀lrc_,普通变量i,ii,index,arg..
12*/
13
14LRC=function(){this.initialize.apply(this,arguments);}
15LRC.prototype={
16 arrLyricTime:[],
17 arrLyric:[],//全局可用,必须执行初始化
18 initialize:function(arg){
19 //私有
20 this.lyricTable=arg.lyricTable;//目标歌词table
21 this.lyricWrapper=arg.lyricWrapper;//目标歌词容器div
22 this.curRowClassName=arg.curRowClassName;//选择行css样式名
23 this.separator=arg.separator;//歌词行分隔符 如:
24 //执行初始化
25 this.arrLyricTime=[];
26 this.arrLyric=[];
27 this.initArray(arg.lyric);
28 this.arrLyricTime=this.sort(this.arrLyricTime);
29 this.setLyricTable(this.arrLyric);
30 },
31 initArray:function(lyric){
32 var lrc_re=new RegExp('\[[0-9:.]*\]','g');//g全局标志,获取所有匹配结果必须
33 var lrc_arr=lyric.split(this.separator);
34 var lrc_temp=0;
35 var lrc_filter=0;//无效行过滤标记
36 for(var i=0;i<lrc_arr.length;i++){
37 var lrc_txt=lrc_arr[i].replace(/\[[\w\W]*\]/g,'').Trim();//add to lyric text array
38 if(lrc_txt==''){
39 lrc_filter++;
40 continue;
41 }
42 this.arrLyric[i-lrc_filter]=lrc_txt;
43 while((lrc_result = lrc_re.exec(lrc_arr[i])) != null){
44 var lrc_second=this.parseSecond(lrc_result.toString().replace(/\[|\]/g,''));
45 if(!isNaN(lrc_second))
46 this.arrLyricTime[lrc_temp++]=(i-lrc_filter)+'|'+lrc_second;//arrLyricTime格式为"行号|秒",如:1|50,2|60
47 }
48 }
49 },
50 /////////////////////////////////////////////////////////////////////////////////////////
51 // 公开函数
52 // IsLyricValid()判断是否合法lrc歌词
53 // DoSync()定位歌词
54 /////////////////////////////////////////////////////////////////////////////////////////
55 IsLyricValid:function(arrLyricTime){
56 return this.arrLyricTime.length>0;
57 },
58 DoSync:function(curPosition){
59 var lrc_times=this.arrLyricTime;
60 for(var i=0;i<lrc_times.length;i++){
61 var lrc_arrPre=lrc_times[i].split('|');
62
63 if(i==0&&curPosition<lrc_arrPre[1]) break;//防止抖动
64
65 if(lrc_times[i+1]==undefined){
66 this.setRow(lrc_arrPre[0]);
67 break;
68 }
69
70 var lrc_arrNext=lrc_times[i+1].split('|');
71 if(curPosition>=lrc_arrPre[1]&&curPosition<lrc_arrNext[1]){
72 this.setRow(lrc_arrPre[0]);
73 break;
74 }
75 }
76 },
77 /////////////////////////////////////////////////////////////////////////////////////////
78 //以下为内部辅助函数
79 /////////////////////////////////////////////////////////////////////////////////////////
80 parseSecond:function(time){
81 try{
82 var lrc_arr=time.split(':');//time格式为时间格式 00:00
83 return parseInt(lrc_arr[0])*60+parseFloat(lrc_arr[1]);
84 }catch(ex){
85 return 0;
86 }
87 },
88 setLyricTable:function(arrLyric){
89 this.lyricWrapper.scrollTop=0;//滚动条置顶
90
91 if(this.lyricTable.rows.length>0){
92 this.clearTable(this.lyricTable);
93 }
94 for(var i=0;i<arrLyric.length;i++){
95 var lrc_tr=this.lyricTable.insertRow();
96 var lrc_cell=lrc_tr.insertCell(0);
97 lrc_cell.innerHTML=arrLyric[i];
98 }
99 },
100 clearTable:function(lyricTable){
101 var lrc_rowNum=lyricTable.rows.length;
102 for (var i=0;i<lrc_rowNum;i++){
103 lyricTable.deleteRow(i);
104 lrc_rowNum=lrc_rowNum-1;
105 i=i-1;
106 }
107 },
108 setRow:function(index){
109 this.setRowClass(index);
110 this.setScroll(index);
111 },
112 setRowClass:function(index){
113 var lrc_rows=this.lyricTable.rows;
114 for(var i=0;i<lrc_rows.length;i++){
115 lrc_rows[i].className='';//TODO:直接添加样式至元素,防止外部css干扰
116 }
117 lrc_rows[index].className=this.curRowClassName;
118 },
119 setScroll:function(index){
120 this.lyricWrapper.scrollTop=this.lyricTable.rows[index].offsetTop-this.lyricWrapper.offsetHeight/3;
121 },
122 sort:function(arrLyricTime){
123 for(var i=0;i<arrLyricTime.length-1;i++){
124 for(var ii=i+1;ii<arrLyricTime.length;ii++){
125 var lrc_cur=parseFloat(arrLyricTime[i].split('|')[1]);
126 var lrc_next=parseFloat(arrLyricTime[ii].split('|')[1]);
127 if(lrc_cur>lrc_next){
128 var lrc_temp=arrLyricTime[i];
129 arrLyricTime[i]=arrLyricTime[ii];
130 arrLyricTime[ii]=lrc_temp;
131 }
132 }
133 }
134 return arrLyricTime;
135 }
136}
137
138
139/////////////////////////////////////////////////////////////
140//外部函数
141/////////////////////////////////////////////////////////////
142String.prototype.Trim = function()
143{
144 return this.replace(/^\s*|\s*$/g,"");
145}
146
1/**//*
2* @author: huangxu
3* @date: 2008-11
4* @site: http://wsky.cnblogs.com
5* @descript: sync display the lyric
6* @usage:
7* //import lrc.css
8* var lrc=new LRC({lyricTable:obj,lyricWrapper:obj,curRowClassName:'xx',lyric:'xxx',separator:'
'});
9* if(lrc.IsLyricValid()) lrc.DoSync(60);
10*
11* @note: 内部变量前缀lrc_,普通变量i,ii,index,arg..
12*/
13
14LRC=function(){this.initialize.apply(this,arguments);}
15LRC.prototype={
16 arrLyricTime:[],
17 arrLyric:[],//全局可用,必须执行初始化
18 initialize:function(arg){
19 //私有
20 this.lyricTable=arg.lyricTable;//目标歌词table
21 this.lyricWrapper=arg.lyricWrapper;//目标歌词容器div
22 this.curRowClassName=arg.curRowClassName;//选择行css样式名
23 this.separator=arg.separator;//歌词行分隔符 如:
24 //执行初始化
25 this.arrLyricTime=[];
26 this.arrLyric=[];
27 this.initArray(arg.lyric);
28 this.arrLyricTime=this.sort(this.arrLyricTime);
29 this.setLyricTable(this.arrLyric);
30 },
31 initArray:function(lyric){
32 var lrc_re=new RegExp('\[[0-9:.]*\]','g');//g全局标志,获取所有匹配结果必须
33 var lrc_arr=lyric.split(this.separator);
34 var lrc_temp=0;
35 var lrc_filter=0;//无效行过滤标记
36 for(var i=0;i<lrc_arr.length;i++){
37 var lrc_txt=lrc_arr[i].replace(/\[[\w\W]*\]/g,'').Trim();//add to lyric text array
38 if(lrc_txt==''){
39 lrc_filter++;
40 continue;
41 }
42 this.arrLyric[i-lrc_filter]=lrc_txt;
43 while((lrc_result = lrc_re.exec(lrc_arr[i])) != null){
44 var lrc_second=this.parseSecond(lrc_result.toString().replace(/\[|\]/g,''));
45 if(!isNaN(lrc_second))
46 this.arrLyricTime[lrc_temp++]=(i-lrc_filter)+'|'+lrc_second;//arrLyricTime格式为"行号|秒",如:1|50,2|60
47 }
48 }
49 },
50 /////////////////////////////////////////////////////////////////////////////////////////
51 // 公开函数
52 // IsLyricValid()判断是否合法lrc歌词
53 // DoSync()定位歌词
54 /////////////////////////////////////////////////////////////////////////////////////////
55 IsLyricValid:function(arrLyricTime){
56 return this.arrLyricTime.length>0;
57 },
58 DoSync:function(curPosition){
59 var lrc_times=this.arrLyricTime;
60 for(var i=0;i<lrc_times.length;i++){
61 var lrc_arrPre=lrc_times[i].split('|');
62
63 if(i==0&&curPosition<lrc_arrPre[1]) break;//防止抖动
64
65 if(lrc_times[i+1]==undefined){
66 this.setRow(lrc_arrPre[0]);
67 break;
68 }
69
70 var lrc_arrNext=lrc_times[i+1].split('|');
71 if(curPosition>=lrc_arrPre[1]&&curPosition<lrc_arrNext[1]){
72 this.setRow(lrc_arrPre[0]);
73 break;
74 }
75 }
76 },
77 /////////////////////////////////////////////////////////////////////////////////////////
78 //以下为内部辅助函数
79 /////////////////////////////////////////////////////////////////////////////////////////
80 parseSecond:function(time){
81 try{
82 var lrc_arr=time.split(':');//time格式为时间格式 00:00
83 return parseInt(lrc_arr[0])*60+parseFloat(lrc_arr[1]);
84 }catch(ex){
85 return 0;
86 }
87 },
88 setLyricTable:function(arrLyric){
89 this.lyricWrapper.scrollTop=0;//滚动条置顶
90
91 if(this.lyricTable.rows.length>0){
92 this.clearTable(this.lyricTable);
93 }
94 for(var i=0;i<arrLyric.length;i++){
95 var lrc_tr=this.lyricTable.insertRow();
96 var lrc_cell=lrc_tr.insertCell(0);
97 lrc_cell.innerHTML=arrLyric[i];
98 }
99 },
100 clearTable:function(lyricTable){
101 var lrc_rowNum=lyricTable.rows.length;
102 for (var i=0;i<lrc_rowNum;i++){
103 lyricTable.deleteRow(i);
104 lrc_rowNum=lrc_rowNum-1;
105 i=i-1;
106 }
107 },
108 setRow:function(index){
109 this.setRowClass(index);
110 this.setScroll(index);
111 },
112 setRowClass:function(index){
113 var lrc_rows=this.lyricTable.rows;
114 for(var i=0;i<lrc_rows.length;i++){
115 lrc_rows[i].className='';//TODO:直接添加样式至元素,防止外部css干扰
116 }
117 lrc_rows[index].className=this.curRowClassName;
118 },
119 setScroll:function(index){
120 this.lyricWrapper.scrollTop=this.lyricTable.rows[index].offsetTop-this.lyricWrapper.offsetHeight/3;
121 },
122 sort:function(arrLyricTime){
123 for(var i=0;i<arrLyricTime.length-1;i++){
124 for(var ii=i+1;ii<arrLyricTime.length;ii++){
125 var lrc_cur=parseFloat(arrLyricTime[i].split('|')[1]);
126 var lrc_next=parseFloat(arrLyricTime[ii].split('|')[1]);
127 if(lrc_cur>lrc_next){
128 var lrc_temp=arrLyricTime[i];
129 arrLyricTime[i]=arrLyricTime[ii];
130 arrLyricTime[ii]=lrc_temp;
131 }
132 }
133 }
134 return arrLyricTime;
135 }
136}
137
138
139/////////////////////////////////////////////////////////////
140//外部函数
141/////////////////////////////////////////////////////////////
142String.prototype.Trim = function()
143{
144 return this.replace(/^\s*|\s*$/g,"");
145}
146