js如何实现歌词
实现歌词滚动的基本思路
歌词滚动功能通常需要同步音频播放时间与歌词显示。核心是将歌词文本按时间轴解析为数组,监听音频播放进度并匹配对应歌词行。
歌词数据格式处理
常见歌词格式为LRC,每行包含时间标签和文本。解析示例:
const lrcText = `[00:01.00]这是第一行歌词
[00:03.50]这是第二行歌词`;
function parseLRC(text) {
const lines = text.split('\n');
return lines.map(line => {
const timeMatch = line.match(/\[(\d{2}):(\d{2})\.(\d{2})\]/);
if (!timeMatch) return null;
const min = parseInt(timeMatch[1]);
const sec = parseInt(timeMatch[2]);
const ms = parseInt(timeMatch[3]);
const time = min * 60 + sec + ms / 100;
const text = line.replace(timeMatch[0], '').trim();
return { time, text };
}).filter(Boolean);
}
歌词同步实现
监听音频时间更新并匹配当前歌词:
const audio = document.querySelector('audio');
const lyrics = parseLRC(lrcText);
let currentLine = 0;
audio.addEventListener('timeupdate', () => {
const currentTime = audio.currentTime;
while (
currentLine < lyrics.length - 1 &&
currentTime >= lyrics[currentLine + 1].time
) {
currentLine++;
}
highlightLyric(currentLine);
});
界面渲染与高亮
动态更新DOM显示当前歌词:
function renderLyrics(container) {
lyrics.forEach((line, index) => {
const div = document.createElement('div');
div.className = 'lyric-line';
div.dataset.index = index;
div.textContent = line.text;
container.appendChild(div);
});
}
function highlightLyric(index) {
document.querySelectorAll('.lyric-line').forEach(el => {
el.classList.remove('active');
});
const activeLine = document.querySelector(`[data-index="${index}"]`);
if (activeLine) activeLine.classList.add('active');
}
平滑滚动优化
添加CSS实现平滑滚动效果:
.lyrics-container {
height: 300px;
overflow-y: auto;
scroll-behavior: smooth;
}
.lyric-line {
padding: 8px;
transition: all 0.3s;
}
.lyric-line.active {
color: #ff0000;
font-size: 1.2em;
}
完整实现示例
结合所有功能的完整示例:
class LyricsPlayer {
constructor(container, audioElement) {
this.container = container;
this.audio = audioElement;
this.lines = [];
this.currentLine = 0;
}
loadLRC(text) {
this.lines = parseLRC(text);
this.render();
}
render() {
this.container.innerHTML = '';
this.lines.forEach((line, index) => {
const div = document.createElement('div');
div.className = 'lyric-line';
div.dataset.index = index;
div.textContent = line.text;
this.container.appendChild(div);
});
this.audio.addEventListener('timeupdate', this.update.bind(this));
}
update() {
const currentTime = this.audio.currentTime;
while (
this.currentLine < this.lines.length - 1 &&
currentTime >= this.lines[this.currentLine + 1].time
) {
this.currentLine++;
}
this.highlight();
}
highlight() {
const activeElement = this.container.querySelector('.active');
if (activeElement) activeElement.classList.remove('active');
const currentElement = this.container.querySelector(
`[data-index="${this.currentLine}"]`
);
if (currentElement) {
currentElement.classList.add('active');
currentElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
}






