需求:当音乐播放到某一句时,出现对应的歌词。
【静态】render歌词
1.拿到LRC歌词(字符串)后string.split('\n')
。每一行生成一个p标签。
2.用正则解析每一行成两部分(时间和歌词)。歌词部分作为p标签的文本,时间部分作为每个p标签data-time属性的值。
3.因为LRC歌词给的时间是形如[01:02.00]的,所以要先解析成和currentTime一样的格式(62.00)
let string = song.lyrics
string.split('\n').map((string)=>{
let p = document.createElement('p')
let regex = /\[([\d:.]+)\](.+)/
let matches =string.match(regex)
if(matches){
p.textContent = matches[2]
let array=matches[1].split(':')
let mins=array[0]
let seconds=array[1]
let newTime=parseInt(mins)*60+parseFloat(seconds)
$(p).attr('data-time', newTime)
}else{
p.textContent = string
}
$('.lines').append(p)
})
【动态】滚动歌词
【Controller】
查看文档,发现audio元素的timeupdate事件可以监听audio元素currentTime(返回以秒为单位的当前媒体元素的播放时间)的每一次改变(MDN解释:The time indicated by the element's currentTime
attribute has changed)
所以,我们监听timeupdate事件,在currentTime每一次改变时,去调用 view的showLyric()方法,同时将currentTime传给View。
$(this.view.el).find('audio').get(0).ontimeupdate=()=>{
let audio=$(this.view.el).find('audio').get(0)
this.view.showLyric(audio.currentTime,this.model.data.currentP)
}
【View】
showLyric(time,lastP){
//找到对应p标签
let $allP=this.$el.find('.lines>p')
let currentP
//当i=$allP.length-1时,$allP.eq(i+1)不存在,会报错,所以要判断一下。
for(i=0;i<$allP.length;i++){
if(i===$allP.length-1){
currentP=$allP[i]
break
}else{
let prevTime=$allP.eq(i).attr('data-time')
let nextTime=$allP.eq(i+1).attr('data-time')
if(prevTime<=time&&time<=nextTime){
currentP=$allP[i]
break
}
}
}
// 高亮p标签
$(currentP).addClass('active').siblings('.active').removeClass('active')
//核心代码:
//如果找到的p标签和上一个一样,说明不需要进行歌词滚动
//如果不一样,则说明需要滚动一行
if(currentP!==lastP){
window.eventHub.emit('selectP',currentP)
this.$el.find('.lines').css('transform',`translateY(${-(24*i-24)}px)`)
}
}
1.遍历p标签,找到当前播放的歌词p标签,currentP。
2.在model的data里添加一个变量currentP,当找到跟currentTime匹配的p标签时,则触发自定义的‘selectP’事件,将该p标签传出去,在controller的里监听‘selectP’事件,将接收到的p标签写到model的data.currentP上。
在调用view.showLyric()时,将model.data.currentP作为第二个参数传过去,和新找到的p标签对比,如果一样,则不滚动歌词,如果不一样,则滚动一行(p标签的line-height)
网友评论