本文案例为歌词滚动,随着音乐播放的进度同步滚动更新。
案例目录结构:

实现源码:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 设置favicon -->
<link rel="shortcut icon" href="./assets/author_favicon.ico" type="image/x-icon">
<!-- 引入css -->
<link rel="stylesheet" href="./css/index.css">
<title>歌词滚动效果</title>
</head>
<body>
<!--
<audio controls src="./assets/海阔天空.mp3"></audio>
-->
<audio controls src="http://media.simoniu.com/海阔天空.mp3"></audio>
<div class="container">
<ul class="lrc-list"></ul>
</div>
<!-- 引入js -->
<script src="./js/mock.js"></script>
<script src="./js/index.js"></script>
</body>
</html>
index.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
li {
list-style: none;
}
body {
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
background-color: #010409;
padding: 50px;
color: #666;
text-align: center;
}
audio {
width: 600px;
}
.container {
width: 100%;
height: 600px;
display: flex;
flex-direction: column;
margin-top: 30px;
overflow: hidden;
}
.container ul {
transition: all 0.3s ease-in;
}
.container li {
transition: all 0.3s ease;
height: 30px;
line-height: 30px;
}
.container li.active {
transform: scale(1.4);
color: #fff;
}
歌词数据 mock.js
const lrc = `[00:00.000] 作词 : 黄家驹\n[00:01.000] 作曲 : 黄家驹\n[00:02.000] 编曲 : Beyond/梁邦彦\n[00:03.000] 制作人 : Beyond/梁邦彦\n[00:18.466]今天我 寒夜里看雪飘过\n[00:25.110]怀着冷却了的心窝漂远方\n[00:30.950]风雨里追赶 雾里分不清影踪\n[00:37.229]天空海阔你与我\n[00:40.291]可会变 (谁没在变)\n[00:43.440]多少次 迎着冷眼与嘲笑\n[00:50.050]从没有放弃过心中的理想\n[00:55.907]一刹那恍惚 若有所失的感觉\n[01:02.133]不知不觉已变淡\n[01:05.243]心里爱 (谁明白我)\n[01:08.801]原谅我这一生不羁放纵爱自由\n[01:15.799]也会怕有一天会跌倒\n[01:22.008]背弃了理想 谁人都可以\n[01:28.276]哪会怕有一天只你共我\n[01:34.102]\n[01:42.695]今天我 寒夜里看雪飘过\n[01:49.284]怀着冷却了的心窝漂远方\n[01:55.189]风雨里追赶 雾里分不清影踪\n[02:01.405]天空海阔你与我\n[02:04.535]可会变 (谁没在变)\n[02:08.014]原谅我这一生不羁放纵爱自由\n[02:15.040]也会怕有一天会跌倒\n[02:21.279]背弃了理想 谁人都可以\n[02:27.531]哪会怕有一天只你共我\n[02:33.633]\n[03:08.454]仍然自由自我 永远高唱我歌\n[03:15.064]走遍千里\n[03:19.739]原谅我这一生不羁放纵爱自由\n[03:26.734]也会怕有一天会跌倒\n[03:33.005]背弃了理想 谁人都可以\n[03:39.257]哪会怕有一天只你共我\n[03:45.496]背弃了理想 谁人都可以\n[03:51.756]哪会怕有一天只你共我\n[03:57.201]原谅我这一生不羁放纵爱自由\n[04:04.204]也会怕有一天会跌倒\n[04:10.456]背弃了理想 谁人都可以\n[04:16.647]哪会怕有一天只你共我\n[04:22.828]\n[04:31.852] 录音 : Shunichi Yokoi\n[04:40.876] 混音 : Shunichi Yokoi\n[04:49.900] 录音室 : Greenbird St./Tokyu Fun St./West Side St.(Tokyo/From Jan/to Apr./1993)\n[04:58.924] 母带工程师 : Setsu Hisai at Tokyu Fun St.\n[05:07.948] 弦乐 : 桑野圣乐团 (Kuwano Strings)\n[05:16.972] OP : Amuse Inc. & Fun House Inc.\n[05:25.996] SP : Amuse H.K. Ltd.`
index.js
const lrcData = formatLrc(lrc)
const audio = document.querySelector('audio')
const container = document.querySelector('.container')
const ul = document.querySelector('.lrc-list')
const containerHeight = container.clientHeight
function formatLrc(data) {
const lrcs = []
const lrc = data.split('\n').forEach(item => {
lrcs.push({
time: formatTime(item),
lrc: item.split(']')[1]
})
})
return lrcs
}
function formatTime(str) {
const res = str.split(']')[0].substring(1)
return res.split(':')[0] * 60 + res.split(':')[1] * 1
}
function monitorAudio() {
const currentAudioTime = audio.currentTime
const index =
lrcData.findIndex(item => {
return item.time > currentAudioTime
}) - 1
return index < 0 ? lrcData.length - 1 : index
}
function createLrcElement() {
const frag = document.createDocumentFragment()
lrcData.forEach(item => {
const li = document.createElement('li')
li.innerText = item.lrc
frag.appendChild(li)
})
ul.appendChild(frag)
}
createLrcElement()
const liHeight = ul.children[0].clientHeight
const maxUpLength = lrcData.length * liHeight - containerHeight
function setUpLength() {
const index = monitorAudio()
let upLength = liHeight * index + liHeight / 2 - containerHeight / 2
if (upLength < 0) {
upLength = 0
}
if (upLength > maxUpLength) {
upLength = maxUpLength
}
ul.style.transform = `translateY(-${upLength}px)`
const lis = [...ul.children]
lis.forEach(item => {
item.classList.remove('active')
})
const li = ul.children[index]
if (li) {
li.classList.add('active')
}
}
audio.addEventListener('timeupdate', function () {
setUpLength()
})
运行效果:
