实现效果
实现分析
解析歌词字符串转化为对象
代码语言:javascript复制var lyrics = `[00:00.000]漂洋过海来看你-孙露
[00:09.480]词:李宗盛
[00:18.960]曲:李宗盛
[00:28.440]为你我用了半年的积蓄
[00:31.980]漂洋过海的来看你
......`;
function parseLyrics() {
let Things = lyrics.split('n');
let result = [];
for (var i = 0; i < Things.length; i ) {
let str = Things[i].split(']');
let timeStr = str[0].substring(1);
let timeArr = timeStr.split(':');
let time = timeArr[0] * 60 timeArr[1];
let obj = {
time: time,
words: str[1],
};
result.push(obj);
}
return result;
}
parseLyrics
函数通过对输入的歌词字符串进行分割和处理,将时间部分转换为以秒为单位的数值,并与对应的歌词文本组合成对象,存储在result
数组中。这样的对象形式方便后续根据时间进行查找和操作。
根据歌词对象和播放器当前播放时间计算当前播放到的歌词
代码语言:javascript复制
function subscript() {
let curTime = doms.audio.currentTime;
for (var i = 0; i < lyricsData.length; i ) {
if (curTime < lyricsData[i].time) {
return i - 1;
}
}
return lyricsData.length - 1;
}
subscript
函数接受当前音频的播放时间curTime
,通过遍历歌词对象数组,比较时间来确定当前播放到的歌词索引。
把解析的歌词内容呈现到页面
代码语言:javascript复制function initLyricsHtml() {
let frag = document.createDocumentFragment();
for (var i = 0; i < lyricsData.length; i ) {
let li = document.createElement('li');
li.textContent = lyricsData[i].words;
frag.appendChild(li);
}
doms.ul.appendChild(frag);
}
initLyricsHtml
函数创建li
元素并填充歌词文本,使用DocumentFragment
来优化性能。DocumentFragment
是一个轻量级的文档片段,它可以容纳多个 DOM 节点,当将其添加到文档中时,只会发生一次页面重绘或回流,从而提高性能。最后将所有li
元素添加到ul
中展示歌词。
根据当前播放到的歌词计算出 ul
向上偏移的位置以及当前高亮歌词
function setOffset() {
let index = subscript();
let offset = liHeight * index liHeight / 2 - containerHeight / 2;
if (offset < 0) {
offset = 0;
}
if (offset > maxOffset) {
offset = maxOffset;
}
doms.ul.style.transform = `translateY(-${offset}px)`;
let liActive = doms.ul.querySelector('.active');
if (liActive) {
liActive.classList.remove('active');
}
let li = doms.ul.children[index];
if (li) {
li.classList.add('active');
}
}
- 首先计算了一些与页面布局相关的高度值,如容器高度、每行歌词高度和最大偏移量。
setOffset
函数根据当前播放的歌词索引计算ul
的偏移量,确保当前歌词在容器中显示合适,并设置当前高亮的歌词行。使用translateY
而不是top
主要是因为translateY
是通过 CSS 变换来实现元素的位移,它在性能上通常比直接修改top
属性更优。translateY
可以利用硬件加速,并且不会引起页面的重布局,从而使页面的渲染更加流畅。
给播放器添加监听器监控 timeupdate
事件,执行 ul
偏移
doms.audio.addEventListener('timeupdate',setOffset);
doms.audio.addEventListener('timeupdate', setOffset);
这行代码使得在音频播放时间不断更新时,能够及时调用setOffset
函数来调整歌词的显示位置和高亮状态。
完整的代码
index.html
<!DOCTYPE html>
<html lang="zh">
<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">
<title>歌词滚动</title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="index.css" />
</head>
<body>
<audio controls src="./孙露-漂洋过海来看你.mp3"></audio>
<div class="container">
<ul class="lrc-list"></ul>
</div>
<script src="data.js"></script>
<script src="index.js"></script>
</body>
</html>
index.css
*{
margin: 0;
padding: 0;
}
body{
background: #000;
color: #666;
text-align: center;
}
audio{
width: 450px;
margin: 30px 0;
}
.container{
height: 420px;
overflow: hidden;
}
.container ul{
transition: 0.6s;
}
.container li {
height: 30px;
line-height: 30px;
transition: 0.2s;
}
.container li.active{
color:#fff;
transform: scale(1.3);
}
index.js
function parseLyrics() {
let Things = lyrics.split('n');
let result = [];
for (var i = 0; i < Things.length; i ) {
let str = Things[i].split(']');
let timeStr = str[0].substring(1);
let timeArr = timeStr.split(':');
let time = timeArr[0] * 60 timeArr[1];
let obj = {
time: time,
words: str[1],
};
result.push(obj);
}
return result;
}
let doms = {
audio: document.querySelector('audio'),
ul: document.querySelector('.container ul'),
container: document.querySelector('.container'),
}
let lyricsData = parseLyrics();
function subscript() {
let curTime = doms.audio.currentTime;
for (var i = 0; i < lyricsData.length; i ) {
if (curTime < lyricsData[i].time) {
return i - 1;
}
}
return lyricsData.length - 1;
}
function initLyricsHtml() {
let frag = document.createDocumentFragment();
for (var i = 0; i < lyricsData.length; i ) {
let li = document.createElement('li');
li.textContent = lyricsData[i].words;
frag.appendChild(li);
}
doms.ul.appendChild(frag);
}
initLyricsHtml();
let containerHeight = doms.container.clientHeight;
let liHeight = doms.ul.children[0].clientHeight;
let maxOffset = doms.ul.clientHeight - containerHeight;
function setOffset() {
let index = subscript();
let offset = liHeight * index liHeight / 2 - containerHeight / 2;
if (offset < 0) {
offset = 0;
}
if (offset > maxOffset) {
offset = maxOffset;
}
doms.ul.style.transform = `translateY(-${offset}px)`;
let liActive = doms.ul.querySelector('.active');
if (liActive) {
liActive.classList.remove('active');
}
let li = doms.ul.children[index];
if (li) {
li.classList.add('active');
}
}
doms.audio.addEventListener('timeupdate',setOffset);