歌词滚动效果的实现

540 阅读1分钟

完成效果

  1. 歌词播放时, 根据播放时间指定对应歌词进行滚动,显示
  2. 效果图: 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. 判断偏移量

  1. 计算规律: image.png

  2. 处理边界情况:

image.png

image.png

/**
处理边界情况: 最小偏移量, 最大偏移量
*/
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)