07-歌词滚动效果

现在学习的代码工作中不一定会需要,如果有,也已经做成了产品和库,

这里锻炼的是思路。

项目效果

 原生JS效率是最高的,框架只是提高代码的可读性

favico图标添加

两种方法

1、放到站点的根目录,但涉及网络和部署相关,不推荐

2、link引入

<link rel="shortcut icon" href="./assets//favicon.ico" type="image/x-icon">

音频控件

<audio controls src="./assets//music.mp3"></audio>

滚动原理

 快速创建lorem(乱数假文)

lorem在HTML中的应用可以生成随机文本

li*30>lorem4

给body设置text-align: center 

text-align: center是可继承的,body→div→ul→li

让它的行盒子元素内容水平居中

控制里层ul的位置

两种思路:

1、margin-top为负(渲染主线程)

但是margin改变会导致reflow(重排)性能低,是在主线程上进行的

 2、用JS控制css3的transform实现位移(合成线程)

css属性变化本身是没有动画效果的

transition过渡,是针对数值类的属性来说的

歌词滚动时,当前歌词高亮并放大,并且与进度条位置一致

JS控制哪一个特殊样式,CSS写样式

放大fontsize会影响布局树,用transform:scale 最好

transition写在li.active还是li?

li.active效果消失没有过渡效果

所以加在li里面

 实现界面交互效果,切入点是什么?

不是怎么设置元素的属性,怎么监控这个音乐播放器的进度

而是从数据切入,我的手上有什么数据,转换成数组或对象

var lrc = `[00:01.06]难念的经
[00:03.95]演唱:周华健
[00:06.78]
[00:30.96]笑你我枉花光心计
[00:34.15]爱竞逐镜花那美丽
[00:36.75]怕幸运会转眼远逝
[00:39.32]为贪嗔喜恶怒着迷`;

此案例中,给的是字符串,想办法变成数组

分割歌词字符串

 理想效果:

[{time: 1.06, words: '难念的经'}, {time: 3.95, words: '演唱:周华健'}]

思路:

  • 根据换行符 \n 分割,将每一行都包装成对象
  • 遍历每个对象,每个对象由时间和歌词键值对组成
  • 歌词时间格式化,统一转换成秒
/**
 * 解析歌词字符串
 * 得到一个歌词对象的数组
 * 每个歌词对象:
 * {time: 开始时间, words: 歌词内容}
 */
function parseLrc () {
	let lines = lrc.split('\n');
	const result = [];
	for (let i = 0; i < lines.length; i++) {
		let str = lines[i];
		let parts = str.split(']');
		let timeStr = parts[0].substring(1); //从第一项开始截取
		const obj = {
			time: parseTime(timeStr),
			words: parts[1]
		}
		result.push(obj);
	}
	return result;
}
/**
 * 时间字符串转换成秒
 * @param {*} timeStr
 * @returns {Number} 小数
 */
function parseTime (timeStr) {
	let parts = timeStr.split(':');
	return +parts[0] * 60 + +parts[1];
}

要高亮显示的歌词,决定了ul列表的偏移量

计算出,当前播放器播到第几秒,对应的高亮歌词下标

获取当前播放时间
audio.currentTime;
遍历歌词数组,判断找到比当前时间大的时间,拿到当前的下标 - 1,就是对应的高亮歌词下标

数据少时,直接在ul中appendChild() :

但是这样频繁改动dom树,70个数据就改动了70多次,数据越多性能越低

function createLrcElements () {
	for (let i = 0; i < lrcData.length; i++) {
		let li = document.createElement('li');
		li.textContent = lrcData[i].words;
		doms.ul.appendChild(li);
	}
}

永远不要率先优化

做代码顺序:数据逻辑→界面逻辑→事件

数据多时 :

利用文档片段过渡

先将li节点插到createDocumentFragment()

再把createDocumentFragment里的东西加到ul里面

function createLrcElements () {
	const frag = document.createDocumentFragment();
	for (let i = 0; i < lrcData.length; i++) {
		let li = document.createElement('li');
		li.textContent = lrcData[i].words;
		// doms.ul.appendChild(li);
		frag.appendChild(li);
	}
	doms.ul.appendChild(frag);
}

createLrcElements();

let maxOffset = doms.ul.clientHeight - containerHeight;

设置ul 元素的偏移量

  • 小于最小值时,让它等于0
  • 大于最大值时,它等于最大值
  • 高亮之前清除所有的高亮
function setOffset () {
	let index = findIndex();
	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 li = doms.ul.querySelector('.active');
	if (li) {
		li.classList.remove('active')
	}
	/* 拿到需要高亮的li标签 */
	li = doms.ul.children[index];
	if (li) {
		li.classList.add('active');
	}
}

猜你喜欢

转载自blog.csdn.net/iaz999/article/details/131363121