css3实现乞丐版前端弹幕效果实现方式及性能优化
1. css3实现乞丐版的弹幕
(1)如何通过css3实现弹幕
首先来看如何通过css的方法实现一个最简单的弹幕:
首先在html中定义一条弹幕的dom结构:
<div class="block">我是弹幕</div>
复制代码
弹幕的移动可以通过移动这个block来实现,以从右向左移动的弹幕为例,弹幕的初始位置在容器的最左侧且贴边隐藏(弹幕的最左边与容器的最右贴合),可以通过绝对定位加transform来实现:
.block{
position:absolute;
}
复制代码
初始位置:
from{
left:100%;
transform:translateX(0)
}
复制代码
移动到最左边的结束位置为(弹幕的最右边与容器的最左边贴合):
to{
left:0;
transform:translateX(-100%)
}
复制代码
起始位置和结束位置的具体图示如下所示:
根据起始位置和结束位置可以定义完整的两帧弹幕动画:
@keyframes barrage{
from{
left:100%;
transform:translateX(0);
}
to{
left:0;
transform:translateX(-100%);
}
}
复制代码
给弹幕元素引入这个动画:
.block{
position:absolute;
/* other decorate style */
animation:barrage 5s linear 0s;
}
复制代码
这样就可以实现一个乞丐版的弹幕效果:

(2)通过绝对定位和left实现弹幕的缺陷
首先明确一下css的渲染过程
- I)根据HTML的结构生成DOM树(DOM树中包含了display:none的节点)
- II)在DOM树的基础上,根据节点的几何属性(margin/padding/width/height/left等)生成render树
- III)在render树的基础上继续渲染color,font等属性
其中如果I)中和II)中的属性发生变化会发生reflow(回流),如果仅仅III)中的属性发生改变,只会发生repaint(重绘)。显然从css的渲染过程我们也可以看出来:reflow(回流)必伴随着重绘。
reflow(回流):当render树中的一部分或者全部因为大小边距等问题发生改变而需要重建的过程叫做回流 repaint(重绘):当元素的一部分属性发生变化,如外观背景色不会引起布局变化而需要重新渲染的过程叫做重绘
reflow(回流)会影响浏览器css的渲染速度,因此在做网页性能优化的时候要减少回流的发生。
在第一节,我们通过left属性,实现了弹幕的效果,left会改变元素的布局,因此会发生reflow(回流),表现在移动端页面上会造成弹幕动画的卡顿。
2. css3弹幕性能优化
我们直到了第一节中的弹幕动画存在卡顿的问题,下面我们看看如何解决动画的卡顿。
(1)CSS开启硬件加速
在浏览器中用css开启硬件加速,使用GPU(Graphics Processing Unit)可以提升网页性能。鉴于此,我们可以发挥GPU的力量,从而使我们的网站或应用表现的更为流畅。
CSS animations, transforms 以及 transitions 不会自动开启GPU加速,而是由浏览器的缓慢的软件渲染引擎来执行。那我们怎样才可以切换到GPU模式呢,很多浏览器提供了某些触发的CSS规则。
比较常见的方式是,我们可以通过3d变化(translate3d属性)来开启硬件加速,鉴于此,我们修改动画为:
@keyframes barrage{
from{
left:100%;
transform:translate3d(0,0,0);
}
to{
left:0;
transform:translate3d(-100%,0,0);
}
}
复制代码
这样就可以通过开启硬件加速的方式,优化网页性能。但是这种方式没有从根本上解决问题,同时使用GPU增加了内存的使用,会减少移动设备的电池寿命等等。
(2)不改变left属性
第二种方法,就是想办法在弹幕动画的前后不改变left属性的值,这样就不会发生reflow。
我们想仅仅通过translateX来确定弹幕节点的初始位置,但是translateX(-100%)是相对于弹幕节点本身的,而不是相对于父元素,因此我们耦合js和css,在js中获取弹幕节点所在的父元素的宽度,接着根据宽度来定义弹幕节点的初始位置。
以父元素为body时为例:
//css
.block{
position:absolute;
left:0;
visibility:hidden;
/* other decorate style */
animation:barrage 5s linear 0s;
}
//js
let style = document.createElement('style');
document.head.appendChild(style);
let width = window.innerWidth;
let from = `from { visibility: visible; -webkit-transform: translateX(${width}px); }`;
let to = `to { visibility: visible; -webkit-transform: translateX(-100%); }`;
style.sheet.insertRule(`@-webkit-keyframes barrage { ${from} ${to} }`, 0);
复制代码
除了耦合js计算了父元素的宽度,从而确定弹幕节点的初始位置之外,这里在弹幕节点中我们为了防止初始位置就有显示,增加了visibility:hidden属性。防止弹幕节点在未确定初始位置时就显示在父容器内。只有弹幕开始从初始位置滚动,才会变得可见。
但是这种css的实现方式,在实现弹幕的扩展功能方面比较麻烦,比如如何控制弹幕暂停等等。
作者:yuxiaoliang
链接:https://juejin.im/post/5b44112cf265da0fa42cc04e
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。