完成效果
- 歌词播放时, 根据播放时间指定对应歌词进行滚动,显示
- 效果图: image.png
代码步骤
1. 获取到的歌词信息 进行格式化操作
/* [00:01.06]难念的经
[00:03.95]演唱:周华健
[00:06.78]
[00:30.96]笑你我枉花光心计
[00:34.15]爱竞逐镜花那美丽
[00:36.75]怕幸运会转眼远逝
[00:39.32]为贪嗔喜恶怒着迷
*/
/**
* 格式化 歌词数据
* 数据类型 => [{ time: xx, content: 'xxx' }]
*/
const formatLyc = () => {
// console.log(lrc)
// 1. 进行换行切割 ['[00:01.06]难念的经', xxx]
const lrcArr = lrc.split('\n')
// console.log(lrcArr);
return lrcArr.map(item => {
const arr = item.split(']')
const timePoint = arr[0].slice(1)
// console.log(timePoint);
const time = timePoint.split(':')
// console.log(time);
const lastTime = (+time[0] * 60 + +time[1])
return {
time: lastTime,
content: arr[1]
}
})
}
// 得到歌词数据
const lycData = formatLyc()
2. 判断当前歌词播放位置
/**
* 返回值为 -1 说明 还没播放第一句
* @param { number } time
* @returns
*/
const getIndex = (time) => {
// 优先判断 当前 播放时间 是否处于最最后一秒
if (time > lycData[lycData.length - 1].time) return lycData.length - 1
// 获取当前 播放的下一条数据 说以 index -1
const index = lycData.findIndex(item => item.time > time)
return index - 1
}
3. 判断偏移量
-
计算规律:
-
处理边界情况:
/**
处理边界情况: 最小偏移量, 最大偏移量
*/
const computedOffset = () => {
const time = audio.currentTime
const index = getIndex(time)
const li = doucument.querySelector('.active')
if (li) li.classList.remove('active')
if(index !== -1) lis[index].classList.add('active')
let offset = (index + 1) * liHeight - markHeight / 2
if (offset < 0) offset = 0
else if (offset > ulHeight - markHeight) offset = ulHeight - markHeight
ul.style.transform = `translateY(-${offset}px)`
}
4.播放事件触发
audio.addEventListener('timeupdate', computedOffset)
完整代码
/**
* 格式化 歌词数据
* 数据类型 => [{ time: xx, content: 'xxx' }]
*/
const formatLyc = () => {
// console.log(lrc)
// 1. 进行换行切割 ['[00:01.06]难念的经', xxx]
const lrcArr = lrc.split('\n')
// console.log(lrcArr);
return lrcArr.map(item => {
const arr = item.split(']')
const timePoint = arr[0].slice(1)
// console.log(timePoint);
const time = timePoint.split(':')
// console.log(time);
const lastTime = (+time[0] * 60 + +time[1])
return {
time: lastTime,
content: arr[1]
}
})
}
const lryData = formatLyc()
console.log(lryData
);
/**
* 判断当前 播放位置 如果 -1 说明 还没播放第一句
* @param { number } time
* @returns
*/
function getIndex(time) {
if (time > lryData[lryData.length - 1].time) return lryData.length - 1
let index = lryData.findIndex(item => item.time > time)
return index - 1
}
const Doms = {
ul: document.querySelector('.show-content ul'),
li : document.querySelectorAll('.show-content ul li')[0],
// showDiv: document.querySelector('.show-content')
mark: document.querySelector('.show-content')
}
const markHeight = Doms.mark.clientHeight
const frag = document.createDocumentFragment()
lryData.forEach(item => {
const li = document.createElement('li')
li.innerHTML = item.content
frag.appendChild(li)
})
Doms.ul.appendChild(frag)
Doms.li = document.querySelectorAll('.show-content ul li')
const liHeight = Doms.li[0].offsetHeight
const aHeight = Doms.ul.clientHeight
const ulHeight = Doms.ul.clientHeight
/**
* 计算元素偏移量
* @param {*} time
*/
// function Domoffset(time) {
// const index = getIndex(time)
// let offset = (index + 1) * liHeight - ulHeight / 2
// const lis = document.querySelector('.active')
// if (lis) lis.classList.remove('active')
// if (index !== -1) Doms.li[index].classList.add('active')
// if (offset < 0) offset = 0
// if (offset > aHeight - ulHeight) offset = aHeight - ulHeight
// Doms.ul.style.transform = `translateY(-${offset}px)`
// }
console.log(ulHeight, markHeight);
const computedOffset = () => {
const time = audio.currentTime
const index = getIndex(time)
const li = document.querySelector('.active')
if (li) li.classList.remove('active')
if(index !== -1) Doms.li[index].classList.add('active')
let offset = (index + 1) * liHeight - markHeight / 2
if (offset < 0) offset = 0
else if (offset > ulHeight - markHeight) {
offset = ulHeight - markHeight
}
Doms.ul.style.transform = `translateY(-${offset}px)`
}
const audio = document.querySelector('audio')
audio.addEventListener('timeupdate', computedOffset)