歌词滚动效果实现
- 歌词滚动效果
- HTML部分
- CSS部分
- JS部分
- 解析歌词字符串,得到歌词的对象数组
- 计算在当前情况下,播放器播放到第几秒的情况
- 创建歌词元素
- 设置ul元素的偏移量
- 最后对时间变化的事件进行监听
- 完整JS代码
歌词滚动效果
- 实现效果如图所示:
首先准备一个歌曲MP3文件,以及歌词,将歌词放入一个
data.js
文件中。
- 歌词内容如下所示:
HTML部分
- 歌词采用ul-li的结构进行展示
- 导入相应的css和js文件
- 代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./css/index.css">
</head>
<body>
<audio src="./assets/薛之谦 - 狐狸.mp3" controls></audio>
<div class="container">
<ul>
</ul>
</div>
<script src="./js/data.js"></script>
<script src="./js/index.js"></script>
</body>
</html>
CSS部分
- 由于CSS属性变化本身没有动画效果,因此这里采用
transition
属性实现其动画效果 transition
属性只针对于数值嘞的属性有效- 代码如下:(大家可以根据自己的喜好设置样式)
*{
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.3s;
}
.container li{
height: 30px;
line-height: 30px;
transition: 0.3s;
list-style: none;
}
.container li.active{
color: #fff;
transform: scale(1.2);
}
JS部分
解析歌词字符串,得到歌词的对象数组
// 解析时间字符串
function parseTime(timeStr){
let parts = timeStr.split(':');
return +parts[0]*60 + +parts[1];
}
/**
* 解析歌词字符串,得到歌词的对象数组
* 每个歌词对象
* {time: 开始时间, words:歌词内容}
*/
function parseLrc(){
var result = [];
var lines = lrc.split('\n'); // 字符串数组
for(let i = 0; i < lines.length; i++){
// 遍历lines
let str = lines[i]; //每一句歌词字符串
let parts = str.split(']'); //分割
let timeStr = parts[0].substring(1); //截取时间
var obj ={
time:parseTime(timeStr),
words: parts[1]
}
result.push(obj);
}
return result;
}
var lrcData = parseLrc();
计算在当前情况下,播放器播放到第几秒的情况
/**
* 计算在当前情况下,播放器播放到第几秒的情况
* lrcData数组中应该高亮显示的歌词下标
*/
function findIndex(){
// 获取播放器的当前时间
let curTime = doms.audio.currentTime;
for(let i = 0; i<lrcData.length; i++){
if(curTime<lrcData[i].time){
return i-1;
}
}
// 如果没有任何一句歌词显示,则返回-1
// 如果找遍了都没有找到,说明播放到最后一句
return lrcData.length-1;
}
创建歌词元素
// 创建歌词元素
function createLrcElement(){
for(let i=0; i<lrcData.length; i++){
let li = document.createElement('li');
li.textContent = lrcData[i].words;
doms.ul.appendChild(li);
}
}
设置ul元素的偏移量
// 容器高度
var containerHeight = doms.container.clientHeight;
// li高度
var liHeight = doms.ul.children[0].clientHeight;
// 最大高度
var maxOffset = doms.ul.clientHeight - containerHeight;
// 设置ul元素的偏移量
function setOffset(){
let index = findIndex();
var offset = liHeight * index + liHeight/2 - containerHeight/2;
if(offset < 0){
offset = 0;
}
if(offset > maxOffset){
offset = maxOffset;
}
doms.ul.style.transform = `translateY(-${offset}px)`;
// 去掉之前的active样式
let li1 = doms.ul.querySelector('.active');
if(li1){
li1.classList.remove('active');
}
let li2 = doms.ul.children[index];
if(li2){
li2.classList.add('active');
}
}
最后对时间变化的事件进行监听
doms.audio.addEventListener('timeupdate', setOffset);
如果大家没有MP3文件或者歌词的话,我会上传我的资源,可以自行下载。
完整JS代码
var doms = {
audio:document.querySelector('audio'),
ul:document.querySelector('.container ul'),
container:document.querySelector('.container')
}
// 解析时间字符串
function parseTime(timeStr){
let parts = timeStr.split(':');
return +parts[0]*60 + +parts[1];
}
/**
* 解析歌词字符串,得到歌词的对象数组
* 每个歌词对象
* {time: 开始时间, words:歌词内容}
*/
function parseLrc(){
var result = [];
var lines = lrc.split('\n'); // 字符串数组
for(let i = 0; i < lines.length; i++){
// 遍历lines
let str = lines[i]; //每一句歌词字符串
let parts = str.split(']'); //分割
let timeStr = parts[0].substring(1); //截取时间
var obj ={
time:parseTime(timeStr),
words: parts[1]
}
result.push(obj);
}
return result;
}
var lrcData = parseLrc();
/**
* 计算在当前情况下,播放器播放到第几秒的情况
* lrcData数组中应该高亮显示的歌词下标
*/
function findIndex(){
// 获取播放器的当前时间
let curTime = doms.audio.currentTime;
for(let i = 0; i<lrcData.length; i++){
if(curTime<lrcData[i].time){
return i-1;
}
}
// 如果没有任何一句歌词显示,则返回-1
// 如果找遍了都没有找到,说明播放到最后一句
return lrcData.length-1;
}
// 创建歌词元素
function createLrcElement(){
for(let i=0; i<lrcData.length; i++){
let li = document.createElement('li');
li.textContent = lrcData[i].words;
doms.ul.appendChild(li);
}
}
createLrcElement()
// 容器高度
var containerHeight = doms.container.clientHeight;
// li高度
var liHeight = doms.ul.children[0].clientHeight;
// 最大高度
var maxOffset = doms.ul.clientHeight - containerHeight;
// 设置ul元素的偏移量
function setOffset(){
let index = findIndex();
var offset = liHeight * index + liHeight/2 - containerHeight/2;
if(offset < 0){
offset = 0;
}
if(offset > maxOffset){
offset = maxOffset;
}
doms.ul.style.transform = `translateY(-${offset}px)`;
// 去掉之前的active样式
let li1 = doms.ul.querySelector('.active');
if(li1){
li1.classList.remove('active');
}
let li2 = doms.ul.children[index];
if(li2){
li2.classList.add('active');
}
}
// 事件监听
doms.audio.addEventListener('timeupdate', setOffset);```