以前在一个移动端的抽奖页面中,在抽奖结果的展现窗口须要弹幕轮播显示,以前踩过一些小坑,如今总结一下前端弹幕效果的实现方式。css
- css3实现乞丐版的弹幕
- css3弹幕性能优化
- canvas实现弹幕
- canva弹幕的扩展功能
本文原文地址在个人博客主页:原文地址html
首先来看如何经过css的方法实现一个最简单的弹幕:前端
首先在html中定义一条弹幕的dom结构:react
<div class="block">我是弹幕</div>
弹幕的移动能够经过移动这个block来实现,以从右向左移动的弹幕为例,弹幕的初始位置在容器的最左侧且贴边隐藏(弹幕的最左边与容器的最右贴合),能够经过绝对定位加transform来实现:css3
.block{ position:absolute; }
初始位置:git
from{ left:100%; transform:translateX(0) }
移动到最左边的结束位置为(弹幕的最右边与容器的最左边贴合):github
to{ left:0; transform:translateX(-100%) }
起始位置和结束位置的具体图示以下所示:web
根据起始位置和结束位置能够定义完整的两帧弹幕动画:canvas
@keyframes barrage{ from{ left:100%; transform:translateX(0); } to{ left:0; transform:translateX(-100%); } }
给弹幕元素引入这个动画:api
.block{ position:absolute; /* other decorate style */ animation:barrage 5s linear 0s; }
这样就能够实现一个乞丐版的弹幕效果:
首先明确一下css的渲染过程
其中若是I)中和II)中的属性发生变化会发生reflow(回流),若是仅仅III)中的属性发生改变,只会发生repaint(重绘)。显然从css的渲染过程咱们也能够看出来:reflow(回流)必伴随着重绘。
reflow(回流):当render树中的一部分或者所有由于大小边距等问题发生改变而须要重建的过程叫作回流
repaint(重绘):当元素的一部分属性发生变化,如外观背景色不会引发布局变化而须要从新渲染的过程叫作重绘
reflow(回流)会影响浏览器css的渲染速度,所以在作网页性能优化的时候要减小回流的发生。
在第一节,咱们经过left属性,实现了弹幕的效果,left会改变元素的布局,所以会发生reflow(回流),表如今移动端页面上会形成弹幕动画的卡顿。
咱们直到了第一节中的弹幕动画存在卡顿的问题,下面咱们看看如何解决动画的卡顿。
在浏览器中用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增长了内存的使用,会减小移动设备的电池寿命等等。
第二种方法,就是想办法在弹幕动画的先后不改变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的实现方式,在实现弹幕的扩展功能方面比较麻烦,好比如何控制弹幕暂停等等。
除了经过css实现弹幕的方法以外,经过canvas也能够实现弹幕。
经过canvas实现弹幕的原理就是时时的重绘文字,下面来一步步的实现。
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
ctx.font = '20px Microsoft YaHei'; ctx.fillStyle = '#000000'; ctx.fillText('canvas 绘制文字', x, y); 上面的fillText就是实现弹幕效果的主要api,其中x表示横方向的坐标,y表示纵方向的坐标,只要时时的改变x,y进行重绘,就能够实现动态的弹幕效果。
ctx.clearRect(0, 0, width, height);
经过定时器,定时改变x,y,每次改变以前先进性清屏,而后根据改变后的x,y进行重绘。当存在多条弹幕的状况下,定义:
let colorArr=_this.getColor(color); 弹幕数组多对应的颜色数组 let numArrL=_this.getLeft(); 弹幕数组所对应的x坐标位置数组 let numArrT=_this.getTop(); 弹幕数组所对应的y坐标位置数组 let speedArr=_this.getSpeed(); 弹幕数组所对应的弹幕移动速度数组
定时的重绘弹幕函数为:
_this.timer=setInterval(function(){ ctx.clearRect(0,0,canvas.width,canvas.height); ctx.save(); for(let j=0;j<barrageList.length;j++){ numArrL[j]-=speedArr[j]; ctx.fillStyle = colorArr[j] ctx.fillText(barrageList[j],numArrL[j],numArrT[j]); ctx.restore(); },16.7);
实现的效果为:
经过canvas实现弹幕的方式,很方便作好比暂停弹幕滚动等扩展功能,此外,也能够给弹幕增长头像,给每条弹幕增长边框等等功能,之后再补充。
最后给一个简单的react弹幕组件;https://github.com/forthealll...