Vue网易音乐歌词处理滚动高亮
对于网易歌词数据我们要先几个步骤处理歌词,并解析拿到我们所需要的数据,因为播放器是全局的,数据就存放再vuex里,拿取数据也方便,其中要对vuex要用一定的了解,vuex中的数据,方法都是通过映射拿到
效果图,部分功能还未完善
歌词处理
-
获取歌词
-
处理歌词,拿到想要的数据
-
渲染
(1)调用网易的歌词api用axios获取数据,因为歌词获取异步,我就把调用写在了vuex中,后续调用更方便,实现复用性
调用formatLyr方法,参数是获取的歌词并把方法返回解析后的歌词存储到state中,下方的数据处理借鉴于黑马程序员
actions:{ async songlyric (context) { //lyricAPI 是封装的axios const { data: res } = await lyricAPI({ id: context.state.playList[context.state.playListIndex].id }) context.commit('updatelyric', await this.dispatch('formatLyr', res.lrc.lyric)) } }(2)定义方法处理歌词 ,处理歌曲时间
actions:{ formatLyr (context, lyricStr) { // 可以看network观察歌词数据是一个大字符串, 进行拆分. const reg = /\[.+?\]/g // const timeArr = lyricStr.match(reg) // 匹配所有[]字符串以及里面的一切内容, 返回数组 // console.log(timeArr) // ["[00:00.000]", "[00:01.000]", ......] const contentArr = lyricStr.split(/\[.+?\]/).slice(1) // 按照[]拆分歌词字符串, 返回一个数组(下标为0位置元素不要,后面的留下所以截取) // console.log(contentArr) const lyricObj = {} // 保存歌词的对象, key是秒, value是显示的歌词 const songSet = [] timeArr.forEach((item, index) => { // 拆分[00:00.000]这个格式字符串, 把分钟数字取出, 转换成秒 const ms = item.split(':')[0].split('')[2] * 60 // 拆分[00:00.000]这个格式字符串, 把十位的秒拿出来, 如果是0, 去拿下一位数字, 否则直接用2位的值 const ss = item.split(':')[1].split('.')[0].split('')[0] === '0' ? item.split(':')[1].split('.')[0].split('')[1] : item.split(':')[1].split('.')[0] // 秒数作为key, 对应歌词作为value lyricObj[ms + Number(ss)] = [contentArr[index], ms + Number(ss)] songSet.push(ms + Number(ss)) // lyricObj[item] = ms + Number(ss) // lyricObj.push({ms + Number(ss)}) }) // 返回得到的歌词对象(可以打印看看 context.commit('updateCurLyric', lyricObj[0][0]) context.commit('updateSongTime', songSet) return lyricObj }, // 格式化播放时长 歌曲时间处理,获取的时间是毫秒 formatDt (context, time) { const dt = time / 1000 let m = parseInt(dt / 60) let s = parseInt(dt % 60) m = m >= 10 ? m : (m = '0' + m) s = s >= 10 ? s : (s = '0' + s) context.commit('updateSongFore', m + ':' + s) } }(3)下面是在定义在state的变量
lyric: {}, // 歌词枚举对象(需要在js拿到歌词写代码处理后, 按照格式保存到这个对象)
curLyric: '', // 当前显示哪句歌词
lastLy: '', // 记录当前播放歌词
currenTime: '', // 当前播放时间
songTime: '', // 歌曲歌词对应时间
songFore: '' // 歌曲时长
(4)定义在mutations中的方法改变state
mutations:{
updatelyric (state, val) {
state.lyric = val
},
updateCurLyric (state, val) {
state.curLyric = val
},
updateLastLy (state, val) {
state.lastLy = val
},
updateCurrenTime (state, val) {
state.currenTime = val
},
updateSongTime (state, val) {
state.songTime = val
},
updateSongFore (state, val) {
state.songFore = val
}
}
数据拿到后可以打印看看解析后的歌词,是以对象存储,key就是每句歌词对应的时间,后面高亮更加方便查询
渲染歌词
:class="{ acter: (currenTime >= item[1]) }" 给已播放的歌词添加样式,当前播放时间大于或等于这句歌词的时间
:style="{ 'font-size': (currenTime >= item[1] && currenTime < songTime[(Number((Object.keys(lyric)).indexOf(i))) + 1]) || curLyric[1] === item[1] ? '.5rem' : '' }" 这是当前正在播放的歌词添加字体变大样式 因为拿到的歌词有些时间对应的歌词为空,所以还要添加判断
<div v-show="show" class="songLyric" @click="show = !show">
<p v-for="(item, i) in lyric" :key="i" :indeX=i :class="{ acter: (currenTime >= item[1]) }"
:style="{ 'font-size': (currenTime >= item[1] && currenTime < songTime[(Number((Object.keys(lyric)).indexOf(i))) + 1]) || curLyric[1] === item[1] ? '.5rem' : '' }">
{{ item[0] }}</p>
</div>
获取当前播放时间
给audio定义一个ref,拿到当前时间并返回vuex 监听了音频播放器的timeupdate事件,以获取当前播放时间。然后,它使用当前时间作为索引来获取对应的歌词。如果当前时间对应的歌词不为空,它会将其更新为当前歌词。如果当前时间对应的歌词为空,它会将上一个非空歌词更新为当前歌词。这样,就可以实现在播放过程中实时切换歌词的效果
showLyric () {
let curTime
// 监听播放audio进度, 切换歌词显示
this.$refs.audio.addEventListener('timeupdate', () => {
// 进度
curTime = Math.floor(this.$refs.audio.currentTime)
this.updateCurrenTime(curTime)
// 避免空白出现
// console.log(this.lyric[curTime])
if (typeof this.lyric[curTime] !== 'undefined' && this.lyric[curTime][0] !== String('\n')) {
this.updateCurLyric(this.lyric[curTime])
this.updateLastLy(this.curLyric)
} else {
this.updateCurLyric(this.lastLy)
}
})
},
现在完成大概就是这样,页面布局看个人
歌词随页面随时间滚动
因为技术有限我的方法是改变父元素的scrollTOP,原则上vue是避免操作DOM的,有人的方法是设置TOP,但是设置了TOP后,就不能自己滚动页面了
歌词随时间滚动,首先就要监听我们拿到的当前播放时间,在watch中监听
因为curLyric中存储的是当前播放的歌词还有当前时间,判断当前时间于当前歌词时间,因为有些歌曲的时间有重复,所以我们先对songTime去重,再用foeEach循环判断,这里用some应该还要好点,看个人选择。(-200 + index * 40)其中-200是刚好到屏幕中间的位置,这个因人而异不重要,index当前判断为true的下标,相当于前面已经播放了多句歌词,再*每句歌词的高度,我高度给的是四十
watch: {
currenTime () {
// 先去重
[...new Set(this.songTime)].forEach((element, index) => {
if (element === this.curLyric[1]) {
document.querySelector('.songLyric').scrollTop = -200 + index * 40
}
})
//下面item是当前播放的时间转换成 00:00格式
let m = parseInt(this.currenTime / 60)
let s = parseInt(this.currenTime % 60)
m = m >= 10 ? m : (m = '0' + m)
s = s >= 10 ? s : (s = '0' + s)
this.item = m + ':' + s
//判断是否播放完,然后执行下一首方法
if (this.item === this.$store.state.songFore) {
this.nextSong()
}
}
},