上一篇
HTML中,通过结合CSS和JavaScript,可以实现歌词滚动播放,使用“标签嵌入音频,利用JavaScript监听音频
播放时间,动态调整歌词列表的位置,并配合CSS实现平滑滚动效果
HTML中实现歌词滚动播放,需要结合HTML、CSS和JavaScript来完成,以下是详细的实现步骤和相关代码示例:
HTML结构搭建
创建一个基本的HTML结构,包含音频播放控件和用于显示歌词的容器。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">歌词滚动播放</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<audio id="audioPlayer" src="song.mp3" controls></audio>
<div class="lyrics-container">
<ul id="lyricsList"></ul>
</div>
<script src="script.js"></script>
</body>
</html>
CSS样式设计
使用CSS来设置歌词容器的样式,包括高度、溢出隐藏等,以及歌词列表项的样式。
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
text-align: center;
}
.lyrics-container {
width: 500px;
height: 200px;
margin: 20px auto;
overflow: hidden;
border: 1px solid #ccc;
background-color: #fff;
}
#lyricsList {
list-style: none;
padding: 0;
margin: 0;
transition: transform 0.5s ease-in-out;
}
#lyricsList li {
height: 30px;
line-height: 30px;
text-align: center;
}
.active {
color: red;
font-weight: bold;
}
JavaScript逻辑实现
JavaScript是实现歌词滚动播放的核心部分,主要负责解析歌词文件、同步音频播放进度、更新歌词显示等。

1 解析歌词文件
歌词文件采用LRC格式,包含时间戳和歌词内容,我们需要编写一个函数来解析这种格式的歌词文件。
function parseLrc(lrcText) {
const lines = lrcText.split('n');
const lyrics = [];
lines.forEach(line => {
const match = line.match(/[(d{2}):(d{2}).(d{2})](.+)/);
if (match) {
const minutes = parseInt(match[1], 10);
const seconds = parseInt(match[2], 10);
const milliseconds = parseInt(match[3], 10);
const time = minutes 60 + seconds + milliseconds / 100;
const word = match[4];
lyrics.push({ time, word });
}
});
return lyrics;
}
2 加载歌词文件
可以使用fetch API来异步加载歌词文件,并将其传递给解析函数。
fetch('lyrics.lrc')
.then(response => response.text())
.then(lrcText => {
const lyrics = parseLrc(lrcText);
renderLyrics(lyrics);
})
.catch(error => console.error('Error loading lyrics:', error));
3 渲染歌词到页面
将解析后的歌词数据动态生成到HTML列表中。

function renderLyrics(lyrics) {
const lyricsList = document.getElementById('lyricsList');
lyricsList.innerHTML = ''; // 清空现有列表
lyrics.forEach(lyric => {
const li = document.createElement('li');
li.textContent = lyric.word;
lyricsList.appendChild(li);
});
}
4 同步音频播放与歌词高亮
通过监听音频的timeupdate事件,获取当前播放时间,并与歌词的时间戳进行比较,找到当前应该高亮的歌词项。
const audioPlayer = document.getElementById('audioPlayer');
let currentIndex = 0;
audioPlayer.addEventListener('timeupdate', () => {
const currentTime = audioPlayer.currentTime;
const lyrics = Array.from(document.querySelectorAll('#lyricsList li'));
let found = false;
for (let i = 0; i < lyrics.length; i++) {
const lyricTime = lyrics[i].dataset.time;
if (currentTime >= lyricTime) {
currentIndex = i;
found = true;
} else {
break;
}
}
if (!found) {
currentIndex = lyrics.length 1;
}
updateLyricsHighlight(currentIndex);
});
5 更新歌词高亮显示
根据当前索引,更新歌词列表项的高亮状态。
function updateLyricsHighlight(index) {
const lyrics = document.querySelectorAll('#lyricsList li');
lyrics.forEach((lyric, i) => {
if (i === index) {
lyric.classList.add('active');
} else {
lyric.classList.remove('active');
}
});
}
处理歌词滚动
当歌词列表项超过容器高度时,需要滚动歌词以保持当前歌词在可视区域内,可以通过调整transform属性来实现平滑滚动。

function scrollLyricsToCurrent() {
const lyricsContainer = document.query.contains('.lyrics-container');
const lyricsList = document.getElementById('lyricsList');
const currentLyric = lyricsList.children[currentIndex];
const containerHeight = lyricsContainer.clientHeight;
const listItemHeight = currentLyric.clientHeight;
const scrollOffset = currentLyric.offsetTop (containerHeight listItemHeight) / 2;
lyricsList.style.transform = `translateY(-${scrollOffset}px)`;
}
在updateLyricsHighlight函数中调用scrollLyricsToCurrent函数,确保每次高亮更新时都检查并调整滚动位置。
完整示例代码整合
将上述各部分代码整合在一起,形成一个完整的实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">歌词滚动播放</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
text-align: center;
}
.lyrics-container {
width: 500px;
height: 200px;
margin: 20px auto;
overflow: hidden;
border: 1px solid #ccc;
background-color: #fff;
}
#lyricsList {
list-style: none;
padding: 0;
margin: 0;
transition: transform 0.5s ease-in-out;
}
#lyricsList li {
height: 30px;
line-height: 30px;
text-align: center;
}
.active {
color: red;
font-weight: bold;
}
</style>
</head>
<body>
<audio id="audioPlayer" src="song.mp3" controls></audio>
<div class="lyrics-container">
<ul id="lyricsList"></ul>
</div>
<script>
function parseLrc(lrcText) {
const lines = lrcText.split('n');
const lyrics = [];
lines.forEach(line => {
const match = line.match(/[(d{2}):(d{2}).(d{2})](.+)/);
if (match) {
const minutes = parseInt(match[1], 10);
const seconds = parseInt(match[2], 10);
const milliseconds = parseInt(match[3], 10);
const time = minutes 60 + seconds + milliseconds / 100;
const word = match[4];
lyrics.push({ time, word });
}
});
return lyrics;
}
fetch('lyrics.lrc')
.then(response => response.text())
.then(lrcText => {
const lyrics = parseLrc(lrcText);
renderLyrics(lyrics);
})
.catch(error => console.error('Error loading lyrics:', error));
function renderLyrics(lyrics) {
const lyricsList = document.getElementById('lyricsList');
lyricsList.innerHTML = ''; // 清空现有列表
lyrics.forEach(lyric => {
const li = document.createElement('li');
li.textContent = lyric.word;
li.dataset.time = lyric.time; // 存储时间戳以便后续使用
lyricsList.appendChild(li);
});
}
const audioPlayer = document.getElementById
