HTML使用js实现的歌词滚动效果

歌词滚动是音乐播放器里最常见的小功能:音乐播一句,歌词高亮一句,当前行始终停在中间。原理其实不复杂——解析 LRC 歌词里的时间戳,跟着 audiotimeupdate 事件走就行。下面给一份纯 HTML + 原生 JS 的完整实现,复制就能用。

一、LRC 歌词格式

LRC 文件每一行的格式是 [分:秒.毫秒]歌词文本,例如:

[00:01.00]Lyrics Demo
[00:03.50]第一句歌词
[00:07.20]第二句歌词
[00:11.80]第三句歌词

二、HTML 结构

一个 audio 播放器,加一个用来装歌词的容器:

<audio id="audio" src="song.mp3" controls></audio>
<div id="lyric" class="lyric">
  <ul id="lyric-list"></ul>
</div>

三、CSS 样式

容器固定高度并隐藏溢出,歌词列表用 transition 做平滑滚动,当前行加 .active 高亮:

.lyric {
  height: 300px;
  overflow: hidden;
  position: relative;
  text-align: center;
}
#lyric-list {
  list-style: none;
  margin: 0;
  padding: 0;
  transition: transform .6s ease-out;   /* 滚动动画 */
}
#lyric-list li {
  height: 30px;
  line-height: 30px;
  color: #888;
  transition: color .3s, font-size .3s;
}
#lyric-list li.active {
  color: #1db954;
  font-size: 18px;
}

四、JavaScript:解析 + 滚动同步

const audio = document.getElementById('audio');
const list  = document.getElementById('lyric-list');
const LINE_HEIGHT = 30;    // 与 CSS 中 li 的高度一致
const CONTAINER_H = 300;   // 与 CSS 中 .lyric 高度一致

let lyrics = [];           // [{ time: 秒, text: '...' }]
let curIndex = -1;

// 1. 解析 LRC 文本
function parseLrc(lrc) {
  const lines = lrc.split('\n');
  const result = [];
  const reg = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/;
  for (const line of lines) {
    const m = line.match(reg);
    if (!m) continue;
    const time = (+m[1]) * 60 + (+m[2]) + (+m[3]) / (m[3].length === 3 ? 1000 : 100);
    const text = line.replace(reg, '').trim();
    if (text) result.push({ time, text });
  }
  return result.sort((a, b) => a.time - b.time);
}

// 2. 渲染歌词列表
function renderLrc() {
  list.innerHTML = lyrics.map(l => `<li>${l.text}</li>`).join('');
}

// 3. 找到当前应高亮的行
function findIndex(time) {
  for (let i = lyrics.length - 1; i >= 0; i--) {
    if (time >= lyrics[i].time) return i;
  }
  return -1;
}

// 4. 跟随播放进度滚动
function update() {
  const index = findIndex(audio.currentTime);
  if (index === curIndex) return;   // 行没变就不动 DOM
  curIndex = index;

  const items = list.children;
  for (let i = 0; i < items.length; i++) {
    items[i].classList.toggle('active', i === index);
  }
  // 让当前行停在容器正中间
  const offset = index * LINE_HEIGHT - CONTAINER_H / 2 + LINE_HEIGHT / 2;
  list.style.transform = `translateY(${-Math.max(offset, 0)}px)`;
}

audio.addEventListener('timeupdate', update);

// 5. 加载歌词(这里写死示例,实际可用 fetch 加载 .lrc 文件)
const lrcText = `
[00:01.00]Lyrics Demo
[00:03.50]第一句歌词
[00:07.20]第二句歌词
[00:11.80]第三句歌词
`;
lyrics = parseLrc(lrcText);
renderLrc();

实现要点

  • 解析时间戳:用正则把 [mm:ss.xx] 拆出来,转成秒数,和歌词文本组成数组并按时间排序。
  • 定位当前行:在 timeupdate 里从后往前找,第一个时间小于等于当前播放时间的行就是当前行。
  • 居中滚动:给歌词容器一个固定高度,通过 transform: translateY() 把整个列表往上推,让高亮行停在中间;用 CSS transition 让滚动平滑过渡。
  • 性能:只在"当前行索引发生变化"时才操作 DOM——timeupdate 一秒会触发好几次,每次都重排会很浪费。

把示例里写死的 lrcText 换成用 fetch 加载的真实 .lrc 文件,再配上自己的歌曲,一个能跟唱的歌词滚动效果就成了。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。

1 条回复

A文章作者M管理员
上传评论图片
  1. Cmlessss
    Cmlessss学前班lv0