css
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 设置标签页图标 -->
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon">
<title>Document</title>
<style>
html {
background: #000;
color: aliceblue;
width: 100%;
height: 100%;
}
body {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
overflow: hidden;
}
ul, li {
margin: 0;
padding: 0;
}
.head {
flex: none;
display: flex;
justify-content: center;
align-items: center;
padding: 30px 0;
}
img {
width: 100px;
height: 100px;
border-radius: 50%;
margin-right: 20px;
}
.right {
display: flex;
flex-direction: column;
justify-content: center;
}
.des {
margin: 6px;
padding-left: 7px;
}
audio {
width: 600px;
height: 60px;
}
.content {
flex: 1;
overflow: hidden;
margin: 0;
}
#list {
list-style: none;
transition: .5s ease;
}
.lyric {
text-align: center;
height: 40px;
line-height: 40px;
transition: 0.2s;
}
.active {
/* font-size: 20px; // 过渡会引起回流,推荐使用tranform */
transform: scale(1.2);
color: aquamarine;
}
</style>
</head>
<body>
<div class="head">
<img src="http://p3fx.kgimg.com/uploadpic/softhead/120/20241213/20241213201306973919.jpg" />
<div class="right">
<p class="des">江语晨-最后一页</p>
<!-- 加controls这个属性才能看到audio标签 -->
<audio controls autoplay src = "https://sharefs.tx.kugou.com/202505141511/3fdf7e70100a2d282f80b08266c04d5d/v3/653e2f76f69079273c79f65fecf58aea/yp/full/ap1000_us0_pi409_s2981929298.mp3">
</div>
</div>
<div class="content">
<ul id="list"></ul>
</div>
<script>
const lyr = "[00:00.00]滴滴音乐网 www.dda5.com\n[00:00.20]\n[00:00.56]雨停滞天空之间\n[00:04.07]像泪在眼眶盘旋\n[00:07.90]这也许是最后一次见面\n[00:15.43]沿途经过的从前\n[00:19.14]还来不及再重演\n[00:23.86]拥抱早已悄悄冷却\n[00:30.84]海潮声 淹没了离别时的黄昏\n[00:38.36]只留下不舍的体温\n[00:45.70]星空下 拥抱着快凋零的温存\n[00:52.96]爱只能在回忆里完整\n[01:03.92]想把你抱进身体里面\n[01:09.45]不敢让你看见\n[01:13.09]嘴角那颗没落下的泪\n[01:19.73]如果这是最后的一页\n[01:24.34]在你离开之前\n[01:28.08]能否让我把故事重写\n[02:38.01]雨停滞天空之间\n[02:41.62]像泪在眼眶盘旋\n[02:45.45]这也许是最后一次见面\n[02:53.00]沿途经过的从前\n[02:56.70]还来不及再重演\n[03:01.47]拥抱早已悄悄冷却\n[03:08.49]海潮声 淹没了离别时的黄昏\n[03:15.89]只留下不舍的体温\n[03:23.26]星空下 拥抱着快凋零的温存\n[03:30.52]爱只能在回忆里完整\n[03:37.83]想把你抱进身体里面\n[03:43.08]不敢让你看见\n[03:46.85]嘴角那颗没落下的泪\n[03:52.90]如果这是最后的一页\n[03:58.10]在你离开之前\n[04:01.81]能否让我把故事重写\n[04:08.04]想把你抱进身体里面\n[04:13.23]不敢让你看见\n[04:16.83]嘴角那颗没落下的泪\n[04:22.88]如果这是最后的一页\n[04:28.12]在你离开之前\n[04:31.83]能否让我把故事重写\n";
const avatar = "http://p3fx.kgimg.com/uploadpic/softhead/120/20241213/20241213201306973919.jpg";
const src = "https://sharefs.tx.kugou.com/202505141511/3fdf7e70100a2d282f80b08266c04d5d/v3/653e2f76f69079273c79f65fecf58aea/yp/full/ap1000_us0_pi409_s2981929298.mp3";
const audio = document.querySelector('audio');
audio.src = src;
const lyrics = lyr.split('\n').filter(v => v).map((item) => {
// const matchs = item.match(/\[(\S+)\](\S*)/);
const [_, time, sentence] = item.split(/[\[\]]/); // 根据[或]符号分割
const [minute, second] = time.split(':'); // 00:30.84
const fTime = +minute * 60 + +second;
return { time: fTime, sentence };
});
console.log('lyrics', lyrics);
const content = document.querySelector('.content');
const list = document.querySelector('#list');
// 创建一个文档片段,把所有dom的操作先应用在这个片段上,之后将片段加入文档,就可以做到将多次dom操作合并成一次,提高效率。
const frag = document.createDocumentFragment();
lyrics.forEach(({ sentence }) => {
const li = document.createElement('div');
//innerText和textContent的区别;attributes和property的区别
li.innerText = sentence;
// add remove toggle
li.classList.add('lyric');
// 操作dom树次数过多,需要优化
frag.appendChild(li);
});
list.appendChild(frag);
// console.dir(list);
audio.play();
// 监听歌曲进度,设置歌词偏移量
const contentHeight = content.clientHeight;
const listHeight = list.clientHeight;
const lyricHeight = list.children[0].clientHeight;
const minOffset = 0;
const maxOffset = listHeight - contentHeight;
const scrollSentence = () => {
const currentTime = audio.currentTime;
const index = lyrics.findIndex(({ time }) => time > currentTime);
const currentIndex = index < 0 ? lyrics.length - 1 : index - 1;
let offset = currentIndex * lyricHeight + lyricHeight / 2 - contentHeight / 2;
if (offset < 0) {
offset = 0;
}
if (offset > maxOffset) {
offset = maxOffset;
}
list.style.transform = `translateY(-${offset}px)`;
const activeLi = list.querySelector('.active');
if (activeLi) {
activeLi.classList.remove('active');
}
const li = list.children[currentIndex];
if (li) {
li.classList.add('active');
}
}
audio.ontimeupdate = (e) => {
scrollSentence();
};
</script>
</body>
</html>
效果: