先放最后效果图。在网页中,上方会有进度条,我们无论是拖动进度条还是它自动播放,都会对应滚动到相应的歌词,当前歌词会加上相应的css效果。并且只展示当前歌词前后一段的内容在页面中间,其他的隐藏。
首先,我们获取歌词的数据,这里是自己整理的数据,具体实际使用中已后端返回的为准。
这里[]中间是歌词的播放具体时间
然后就是搭建静态页面
<!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">
<title>Document</title>
<link rel="shortcut icon" href="./assets/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<!-- 这里没有找到原版的mp3资源,所以翻唱版本存在音频不同步问题。但不影响功能的实现 -->
<audio controls src="./assets/music.mp3"></audio>
<div class="container">
<ul class="lrc-list">
</ul>
</div>
<script src="./js/data.js"></script>
<script src="./js/index.js"></script>
</body>
</html>
* {
margin: 0;
padding: 0;
}
body {
background-color: #000;
color: #666;
text-align: center;
}
audio {
width: 450px;
margin: 30px 0;
}
.container {
height: 420px;
overflow: hidden;
}
.container ul {
transition: 0.6s;
list-style: none;
}
.container li {
height: 30px;
line-height: 30px;
transition: 0.2s;
}
.container li.active {
color: #fff;
/* font-size: 40px; */
transform: scale(1.2);
}
有了静态页面以后就是对歌词数据进行操作。无非就是先拿到数据(歌词内容+时间),然后将时间转换成秒以方便对应歌词,计算播放器当前时间所对应的歌词以进行滚动操作
// console.log(lrc);
/**
* 解析歌词字符串
* 得到一个歌词对象的数组
* 每个歌词对象: {time: 开始时间,words: 歌词内容}
*/
function parseLrc(){
var lines = lrc.split('\n');
var result = [];
for(var i = 0;i < lines.length; i++){
var str = lines[i];
var parts = str.split(']');
var timeStr = parts[0].substring(1);
var obj = {
time: parseTime(timeStr),
words: parts[1]
}
result.push(obj);
}
return result;
}
/**
* 将一个时间字符串解析成数字(秒)
*/
function parseTime(timeStr){
var parts = timeStr.split(':');
return +parts[0] * 60 + +parts[1];
}
var lrcData = parseLrc();
// console.log(lrcData);
// 获取需要的dom
var doms = {
audio: document.querySelector('audio'),
ul: document.querySelector('.container ul'),
container: document.querySelector('.container')
}
/**
* 计算出在当前播放器播放到第几秒的情况下,
* lrcData数组中,应高亮显示的歌词下标,
* 若没有任何一句歌词需要显示,得到-1
*/
function findIndex(){
var curTime = doms.audio.currentTime;
for(var i = 0;i < lrcData.length; i++){
if(curTime < lrcData[i].time){
return i - 1;
}
}
// 找遍了还没找到,即最后一句
return lrcData.length - 1;
}
// findIndex();
// 界面
/**
* 创建歌词元素li
*/
function createLrcElements(){
var frag = document.createDocumentFragment(); // 文档片段
for(var i = 0; i < lrcData.length; i++){
var li = document.createElement('li');
li.textContent = lrcData[i].words;
//doms.ul.appendChild(li); //改动了dom树
frag.appendChild(li);
}
doms.ul.appendChild(frag);
}
createLrcElements();
// 容器高度
var containerHeight = doms.container.clientHeight;
// 每个li的高度
var liHeight = doms.ul.children[0].clientHeight;
// 最大偏移量
var maxOffset = doms.ul.clientHeight - containerHeight;
/**
* 设置ul元素的偏移量
*/
function setOffset(){
var index = findIndex();
var offset = liHeight * index + liHeight/2 - containerHeight/2;
offset = offset < 0 ? 0 : offset;
offset = offset > maxOffset ? maxOffset : offset;
doms.ul.style.transform = `translateY(-${offset}px)`
// 去掉之前的active样式
var li = doms.ul.querySelector('.active')
if(li){
li.classList.remove('active')
}
li = doms.ul.children[index];
if(li){
li.classList.add('active');
}
}
doms.audio.addEventListener('timeupdate',setOffset)