当前位置: 首页 > Web前端 > CSS

强大的CSS3-JS-帧动画的多种实现方式与性能对比

时间:2023-03-30 18:07:30 CSS

强大的CSS3/JS:帧动画的多种实现方式及性能比较\.JS动画(操作DOM,修改CSS属性值)3\.画布动画4\.SVG动画5\.以三人为首的3D动画。就是帧动画,也叫序列帧动画、定格动画、逐帧动画等,这里统一用帧动画来表达。应用场景帧动画一般用来实现稍微复杂一点的动画效果。同时也希望动画更加细腻,设计师可以发挥的更加自如。它可以定义每个时间刻度上的显示内容。我们一般使用帧动画来做页面加载、小人物、小物体元素的简单动画。我们想象中的帧动画应该具备以下特点:可以自由控制播放、暂停和停止,可以控制播放次数,可以在播放速度上添加交互,可以在播放完成后添加事件。先由设计师在PS中的时间轴上进行设计,然后将图片导出给前端人员。PS时间轴动画一般用于制作稍微简单一些的动画,操作简单方便。或者是设计师在AE的时间轴里设计的,因为AE内置了更丰富的动作效果,比如转换,翻转等,AE可以帮我们实现更复杂的效果,然后把图片导出到前台——端人员。在这里,帧动画素材要求,每一帧的图片最好宽高是偶数,张数是偶数,周围最好有一些空白。实现方案目前想到的方案整理如下图,我们将详细介绍每一种方案。1、GIF图片我们可以将上面制作的帧动画导出为GIF图片。GIF图像将连续播放,不能暂停。常用于实现小细节动画,成本低,使用方便。但它的缺点也很明显:在图像质量方面,gif支持的颜色少(最多256色),alpha透明度支持差,图像边缘锯齿严重;交互方面,不能直接控制播放、暂停、播放次数,灵活性差;performance另一方面,gif会导致页面周期性绘制,性能较差。2、CSS3框架动画CSS3框架动画是我们今天需要重点关注的解决方案。核心是利用CSS3中的Animation动画,准确的说是利用animation-timing-function的步长函数steps(number_of_steps,direction)来实现动画的逐帧连续播放。帧动画的实现原理是在视觉中不断切换画面内容,利用视觉滞留的生理现象,实现连续播放的动画效果。下面介绍几种制作CSS3帧动画的方案。(1)不断切换动画图片地址src(不推荐)。我们把图片放在元素的背景中(background-image),通过改变background-image的值来切换frame。但是这种方法有以下缺点,所以不推荐这种方案。多张图片会带来多个HTTP请求。每张图片第一次加载会导致切换图片时出现闪烁,不利于文件管理(2)不断切换精灵图片的位置(推荐)。我们将动画图片的所有帧合并为一张精灵图,通过改变background-position的值来实现动画帧的切换。分为两步:第一步:将动画帧合并成精灵图。精灵图的要求可以参考上面的素材准备。例如,下面的帧动画精灵图像共有20帧。[图片上传失败...(image-91f7b7-1556627487319)]

第二步:使用steps步进函数切换Sprite图片的位置先看写法一:
.sprite{width:300px;高度:300px;背景重复:不重复;背景图像:url(frame.png);动画:帧333ms步长(1,结束)都是无限的;}@keyframes帧{0%{background-position:00;}5%{background-position:-300px0;}10%{background-position:-600px0;}15%{背景位置:-900px0;}20%{背景位置:-1200px0;}25%{背景位置:-1500px0;}30%{背景位置:-1800px0;}}35%{background-position:-2100px0;}40%{background-position:-2400px0;}45%{background-position:-2700px0;}50%{background-position:-3000px0;}55%{background-position:-3300px0;}60%{background-position:-3600px0;}65%{background-position:-3900px0;}70%{background-position:-4200px0;}75%{背景位置on:-4500px0;}80%{background-position:-4800px0;}85%{background-position:-5100px0;}90%{background-position:-5400px0;}95%{background-position:-5700px0;}100%{background-position:-6000px0;}}对上面的动画有疑问吗?问题1:既然已经详细定义了关键帧,是否可以使用steps函数代替直接定义线性变化?动画:帧10s线性无限;如果我们这样定义的话,动画就不会是一步步的,而是会不断的改变背景图的位置,这是移动的效果,不是切换,如下图:问题2:应该不设置为20步?怎么变成1了?这里我们先了解一下animation-timing-function属性CSSanimation-timing-function属性定义了CSS动画在每个动画周期中执行的节奏。对于关键帧动画,定时函数作用于一个关键帧周期而不是整个动画周期,即从关键帧开始到关键帧结束为止。计时函数作用于每两个关键帧之间,而不是整个动画。接下来我们看一下steps()函数:steps函数指定了一个step函数,它接受两个参数。第一个参数接受一个整数值,指示两个关键帧之间的步数。第二个参数有两个值。默认为<结束>。step-start等同于step(1,start)。step-end等价于step(1,end)。综上所述,我们可以知道,因为我们详细定义了一个关键帧循环,从开始到结束,每两个关键帧一步显示,也就是说在0%到5%之间变化一次,而5%~10%变化一次,这样写就可以达到想要的效果了。看第二种写法:
.sprite{width:300px;高度:300px;背景重复:不重复;背景图像:url(frame.png);animation:frame333mssteps(20)bothinfinite;}@keyframesframe{0%{background-position:00;}//100%可以省略{background-position:-6000px0;}}这里定义开始而End的意思是定义一个关键帧周期,但是因为我们没有详细定义每一帧的显示,所以我们要将0%到100%的区间分成20个step来分阶段显示。也可以改为关键字的写法,只能定义最后一帧,因为默认第一帧是初始位置。@keyframesframe{from{background-position:00;}//可以省略到{background-position:-6000px0;}}(3)连续移动spritemap的位置(移动端推荐)为与第二种基本相同,只是将精灵贴图位置切换的过程换成了transform:translate3d(),只是需要加一个overflow:hidden;集装箱包装。这里我们仅以定义初始帧和结束帧为例。使用transform可以开启GPU加速,提升机器渲染效果,有效解决移动端帧动画抖动问题。
.sprite-wp{width:300px;高度:300px;溢出:隐藏;}.sprite{宽度:6000px;高度:300px;will-change:变换;背景:url(frame.png)无重复中心;动画:frame333mssteps(20)bothinfinite;}@keyframesframe{0%{transform:translate3d(0,0,0);}100%{transform:translate3d(-6000px,0,0);}}3.JS帧动画(一)用JS控制img的src属性切换(不推荐)和上面的CSS3帧动画元素background-image属性相同,会出现多次请求等问题,不推荐这个解决方案,但这是一个解决方案。(2)使用JS控制Canvas图像绘制。通过Canvas制作帧动画的原理是使用drawImage方法在Canvas上绘制图片,通过不断的擦除和重绘得到我们想要的效果。(function(){vartimer=null,canvas=document.getElementById("canvas"),context=canvas.getContext('2d'),img=newImage(),width=300,height=300,k=20,i=0;img.src="frame.png";functiondrawImg(){context.clearRect(0,0,宽度,高度);i++;如果(i==k){i=0;}context.drawImage(img,i*width,0,width,height,0,0,width,height);window.requestAnimationFrame(drawImg);}img.onload=function(){window.requestAnimationFrame(drawImg);}})();动画效果通过改变裁剪图片的X坐标位置,或者通过改变图片放置在canvas上的坐标Position实现,如下:context.drawImage(img,0,0,width*k,height,-i*宽度,0,宽度*k,高度);.(3)利用JS控制CSS属性值的变化这种方法和前面的CSS3帧动画一样,有3种方式,一种是通过JS切换元素背景图地址background-image,另一种是通过JS-position切换元素背景图片位置背景,最后一个是通过JStransform:translate3d()移动元素,第一个就不介绍了,因为也会有多次请求等问题,不推荐使用,这里实现后两者。切换元素背景图片位置background-position.sprite{width:300px;高度:300px;背景:url(frame.png)no-repeat00;(function(){varsprite=document.getElementById("sprite"),picWidth=300,k=20,i=0,timer=null;//重置背景图片位置sprite.style="background-position:00";//改变背景图片的位置functionchangePosition(){sprite.style="background-position:"+(-picWidth*i)+"px0";i++;if(i==k){i=0;}window.requestAnimationFrame(changePosition);}window.requestAnimationFrame(changePosition));})();移动元素背景图片位置transform:translate3d().sprite-wp{width:300px;高度:300px;溢出:隐藏;}.sprite{宽度:6000px;高度:300px;will-change:变换;背景:url(frame.png)无重复中心;(function(){varsprite=document.getElementById("sprite"),picWidth=300,k=20,i=0,timer=null;//重置背景图片的位置sprite.style="transform:translate3d(0,0,0)";//改变背景图片移动functionchangePosition(){sprite.style="transform:translate3d("+(-picWidth*i)+"px,0,0)";i++;if(i==k){i=0;}window.requestAnimationFrame(changePosition);}window.requestAnimationFrame(changePosition);})();方案总结总结以上方案,我们可以看出GIF图片具有一定的优势,同时缺点和局限性也很明显,因此该方案依赖于其他实现的性能。让我们比较一下。如果测试结果出现偏差,可能与测试环境的变化有关。测试环境:系统:Windows10专业版处理器:Intel(R)Core(TM)i7-6700CPU@3.40GHz3.41GHzRAM:8.00GB浏览器:Chrome72.0CSStransform:translate3d()该方案的性能数据如下所示上图。通过Chrome浏览器的各种工具,我们查看了各个方案的FPS、CPU占用率、GPU占用率、Scripting、Rendering、Painting、内存占用情况,得到如下数据:performance-schemecssbackground-positioncsstransform:translate3d()JSCanvasJSbackground-positionJStransform:translate3d()FPS6051606060CPU5%-6.2%0.3%-1%7%-8%6%-8%6%-8%GPU3.8MB4-10MB03.8MB4-11MBScripting002.51%2.61%3.18%渲染1。17%0.141%0.84%1.65%2.71%Painting1.58%0.01%1.63%1.75%1.05%Memory20112K21120K21588K20756K21576K通过分析以上数据,我们可以得出以下几点:除了csstransform:translate3d()方案,FPS其他方案都是可以达到60FPS的流畅度,但是这个方案的FPS并不是很低。CPU使用率最低的解决方案是csstransform:translate3d()解决方案。GPU使用率最低的方案是JSCanvas绘图方案。没有脚本开销的CSS解决方案渲染最少的解决方案是csstransform:translate3d()解决方案。最少的绘画是csstransform:translate3d()方案。每个程序的内存使用情况相差不大。结论:我们看到在7个指标中,csstransform:translate3d()解决方案最小化了其中的4个。从这个角度来看,我们完全有理由选择这个解决方案来实现CSS框架动画。至于其他方案的绝对对比,暂时还不能给出结论。选择取决于具体情况,也取决于开发者追求的性能指标。引申一下我们的网页动画,每一种动画形式都有自己的优势。比如大量粒子效果的Canvas绘制方案肯定比DOM+CSS实现要好。大量的CSS属性值转换使用transform来实现性能。是要更好。注意事项:动画画面的宽高最好为偶数,总帧数为偶数,画面拼接处最好有一定的留白。适配:移动端适配最好不要使用rem,因为rem的计算会导致小数点四舍五入,产生一定的抖动效果。建议直接以px为单位,同时辅助scale(缩放)媒体查询进行适配。如果使用rem适配,尝试使用transform方案,可以优化解决抖动问题。Tips:使用will-change,在元素属性真正发生变化之前,提前做好相应的准备工作。总结一下这篇文章,我们主要梳理了目前实现帧动画的几种方案,同时实现了各种方案的效果,讨论了它们的优缺点,并比较了它们的性能。transform:translate3d()方案在实现和性能方面明显优于其他方案。