效果展示
跟随歌曲时间同步滚动歌词,并实现高亮效果
总体思路
- 获取歌词
- 解析歌词
- 打印歌词
- 同步歌词
1. 获取歌词
用 ajax 和网易云的 api 获取的歌词资源.(涉及到跨域问题,这里不赘述,主要讲同步功能的思路)
2. 解析歌词
步骤:
1. 新建数组 lrcArray
2. 提取歌词 lrcGet
3. 用换行符把字符串 lrcGet分 割为数组 lrc
4. 遍历 lrc
其中,遍历 lrc 后的处理步骤
- 过滤
- 提取和转化时间
- 提取歌词
- 添加进数组 lrcArray
控制台返回的 lrcGet
lrc
js
var lrcArray = [];//新建数组,用于存放歌词
var lrcGet = data.lrc.lyric;//提取歌词
// console.log(lrcGet);
var lrc = lrcGet.split(\'\n\');
// console.log(lrc);
$.each(lrc, function(i, item) {
//过滤空白文本
if (item.split(\']\')[1] == "" || item == "" || item.indexOf(\'作曲\') !== -1 || item.indexOf(\'作词\') !== -1) {
return true;
}
//转化时间
var timeStr = item.substring(item.indexOf("[") + 1, item.indexOf("]"));
var min = parseInt(timeStr.split(\':\')[0]) * 60;
var sec = parseFloat(timeStr.split(\':\')[1]);
var time = parseFloat((min + sec).toFixed(2));
//添加进数组
lrcArray.push({
t: time,
c: item.substring(item.indexOf(\']\') + 1)
});
});
3. 打印歌词
控制台返回处理后的数组lrcArray如下:
html代码
<div class="lyrics">
<ul class="lyricsList"></ul>
</div>
js
//显示歌词
//打印全部在页面
var html = "";
$.each(lrcArray, function(i, v) {
html += \'<li>\' + v.c + \'</li>\';
});
$(\'.lyricsList\').append(html);
4. 同步歌词
$(\'#audio\')[0].ontimeupdate = function() {
$.each(lrcArray, function(i, v) {
if ($(\'#audio\')[0].currentTime >= lrcArray[i].t) {
$(\'.lyricsList\').css(\'margin-top\', \'\');//避免进度变动时数值产生混乱
$(\'.lyricsList li\').eq(i).addClass(\'highlight\');
$(\'.lyricsList li\').eq(i).siblings().removeClass(\'highlight\');
if (i > 2) {
$(\'.lyricsList\').css(\'margin-top\', (-i + 2) * 30 + \'px\');
}
}
});
};
思路: audio 时间进度每更新一次,就同步一次该数组,把audio 的当前时间和数组每一项的时间作比较.当检测到前者大于或等于后者,就高亮和滚动对应的歌词.
关于高亮的思路
以代码的角度:
以index(这里对应 i)为桥梁,只要符合条件,对应的 li 就会先被高亮后被消除高亮,一直到最后一个符合条件的 lrcArray[i].t
里的 i对应的 li被高亮,此时只会高亮而不会消除,等到 i+1 符合条件,i 的 先高亮后消除,i+1只高亮,以此迭代下去.
从样式上看:
由于代码执行飞速,以致肉眼不会看到先高亮后消除的过程,只会看到最后一个被高亮.
关于滚动的思路
html代码
<div class="lyrics">
<ul class="lyricsList"></ul>
</div>
css 样式
.lyrics {
height: 32%;
padding-top: 8px;
overflow: hidden;
}
.lyrics .lyricsList {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.lyrics .lyricsList li {
height: 30px;
line-height: 15px;
font-size: 12px;
}
js 代码
var index = 0;
$(\'#audio\')[0].ontimeupdate = function() {
if (this.currentTime >= parseFloat(lrcArray[index].t)) {
$(\'.lyricsList li\').eq(index).addClass(\'highlight\');
$(\'.lyricsList li\').eq(index).siblings().removeClass(\'highlight\');
if (index > 2) {
//添加 css 样式以实现滚动效果
$(\'.lyricsList\').css(\'margin-top\', -(index - 2) * 30 + \'px\');
}
index++;
}
};
js 滚动思路分析图
从 i>2 起设置margin-top负值.
当滚动到 i=3对应的 li 节点后,margin-top 的值每次增加一个负的 li 的行高,即-30px,以实现推动效果。在高亮效果滚动到下一个 li的同时,ul 容器往上推动一个li行高,两者作用下的效果为高亮位置保持不变。
注:
-
在条件代码块的第一行用
.css(\'margin-top\', \'\')
消除样式,避免每一次margin-top 值发生改变引起数值错乱,避免 bug. -
以下在触发进度条事件时,会发生同步错误.
原因:
当this.currentTime
突然变小,而 index 无法随之改变,因此条件无法匹配.
当this.currentTime
突然变大, index也无法突变,只能递增,会产生逐行高亮又消失,直到匹配到临界值的情况.
涉及的 jq 方法总结
- dom 方法
- $(dom).eq(i) 返回第i 个;
- $(dom).siblings() dom的兄弟节点,即除了自己,借助该方法可实现兄弟节点之前的排他性样式,如歌词高亮.
- $(dom).empty() 清空所有子节点
- $(dom).css(\'属性\', \'\'); 消除某个属性
- each遍历中跳出循环的方法:
- return false;——跳出所有循环;相当于 javascript 中的 break 效果。
- return true;——跳出当前循环,进入下一个循环;相当于 javascript 中的 continue 效果
- 过滤空白文本和包含关键词文本
if (item.split(\']\')[1] == "" || item == "" || item.indexOf(\'作曲\') !== -1 || item.indexOf(\'作词\') !== -1) {
return true;
- 字符串
- str.split(\'\n\')
- str.substring(str.indexOf("[") + 1, str.indexOf("]")) 提取[]中间的字符串
- str.indexOf(str,str)
- 数字
- str.parseInt(),str.parseFloat()
- num.toFixed(num)
- 数组
- arr.push(item)
完整代码
<div class="lyrics">
<ul class="lyricsList"></ul>
</div>
.lyrics {
height: 32%;
padding-top: 8px;
overflow: hidden;
}
.lyrics .lyricsList {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.lyrics .lyricsList li {
height: 30px;
line-height: 15px;
font-size: 12px;
}
//歌词高亮
.highlight {
color: #3effee;
text-shadow: 0 0 10px #ded1fc;
}
function parseLrc(data) {
// console.log(data);
var lrcArray = []; //新建数组,用于存放歌词
var lrcGet = data.lrc.lyric; //提取歌词
console.log(lrcGet);
var lrc = lrcGet.split(\'\n\');
console.log(lrc);
$.each(lrc, function(i, item) {
//过滤空白文本
if (item.split(\']\')[1] == "" || item == "" || item.indexOf(\'作曲\') !== -1 || item.indexOf(\'作词\') !== -1) {
return true;
}
//转化时间
var timeStr = item.substring(item.indexOf("[") + 1, item.indexOf("]"));
var min = parseInt(timeStr.split(\':\')[0]) * 60;
var sec = parseFloat(timeStr.split(\':\')[1]);
var time = parseFloat((min + sec).toFixed(2));
//添加进数组
lrcArray.push({
t: time,
c: item.substring(item.indexOf(\']\') + 1)
});
});
console.log(lrcArray);
//显示歌词
var html = "";
$.each(lrcArray, function(i, v) {
html += \'<li>\' + v.c + \'</li>\';
});
$(\'.lyricsList\').append(html);
//同步高亮歌词
$(\'#audio\')[0].ontimeupdate = function() {
$.each(lrcArray, function(i, v) {
if ($(\'#audio\')[0].currentTime >= lrcArray[i].t) {
$(\'.lyricsList\').css(\'margin-top\', \'\');
$(\'.lyricsList li\').eq(i).addClass(\'highlight\');
$(\'.lyricsList li\').eq(i).siblings().removeClass(\'highlight\');
if (i > 2) {
$(\'.lyricsList\').css(\'margin-top\', (-i + 2) * 30 + \'px\');
}
}
});
};
}
本文作者: 乔一亖
本文链接: https://www.cnblogs.com/joyce33/p/13376752.html
版权声明: 本文版权归作者和博客园共有,转载请注明出处!如有问题或建议,请多多赐教,非常感谢。