动画性能优化-requestanimationframeGPU等
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动画性能优化-requestanimationframeGPU等相关的知识,希望对你有一定的参考价值。
最近在做一个场景动画,有一个欢迎界面和一个主动画界面,两个界面之间的连接通过一个进度条来完成,当进度条完成,提供通往主动画的按钮。
画面会从一个个的场景移动过去,用户可通过点击抽奖、查看气泡商铺等进行交互,同时可拖动画面,前移或后退。该项目中,出了主动画,还有人物场景对话的动画等,性能的优化、用户的体验变得尤为重要,这里总结一下在开发过程中使用的一些性能、体验优化方法。
1、动画
a、优先采用requestanimationframe,实现动画帧的并发渲染;
b、做减法:兼容低版本浏览器(a中的元素不生效,通过setTimeout实现动画),保留主动画性能,去除重要性不大的动画(跑马灯、过程小动画等);
c、大图动画性能消耗非常大,使用translate3d实现GPU加速,动画结束、暂停时,切换回2d,取消加速;
d、按需加载/卸载动画;
a、优先采用requestanimationframe,实现动画帧的并发渲染;
b、做减法:兼容低版本浏览器(a中的元素不生效,通过setTimeout实现动画),保留主动画性能,去除重要性不大的动画(跑马灯、过程小动画等);
c、大图动画性能消耗非常大,使用translate3d实现GPU加速,动画结束、暂停时,切换回2d,取消加速;
d、按需加载/卸载动画;
e、每个动画帧处理函数简化,尽量减少或者去除回流、重绘。
2、加载、用户体验优化
a、首屏优先加载,保证用户体验的流畅性:优先欢迎界面(即首屏)图片资源的加载,所有图片loaded以后,再启动主动画资源的加载,与进度条动画;
b、资源的预加载:在进入主动画之前,进行主动画各资源的加载,当完成加载时,再promise结束进度条动画;
a、首屏优先加载,保证用户体验的流畅性:优先欢迎界面(即首屏)图片资源的加载,所有图片loaded以后,再启动主动画资源的加载,与进度条动画;
b、资源的预加载:在进入主动画之前,进行主动画各资源的加载,当完成加载时,再promise结束进度条动画;
c、常规优化:雪碧图、压缩、base64等;
d、存储dom变量,减少dom tree的查找等;
e、限频。
3、说明
3.1、requestAnimationFrame
它是一种高级的方法,存在兼容性问题。主要原理是浏览器并行的渲染n个requestAnimationFrame,而且这种渲染是在浏览器自身渲染的同时发生,非常节省性能。相对setTimeout,是一个js的执行栈,只能串行执行,并且会影响其他js的处理。所以,使用前者,性能更佳,更流畅,交互体验更佳。特别是多个动画同时进行时,前者毫无压力,后者表示卡顿厉害。兼容代码如下,引自张鑫旭的log,感兴趣的同学可以仔细读一下:链接
1 /* requestAnimationFrame.js 2 * by zhangxinxu 2013-09-30 3 */ 4 (function() { 5 var lastTime = 0; 6 var vendors = [‘webkit‘, ‘moz‘]; 7 for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 8 window.requestAnimationFrame = window[vendors[x] + ‘RequestAnimationFrame‘]; 9 window.cancelAnimationFrame = window[vendors[x] + ‘CancelAnimationFrame‘] || // name has changed in Webkit 10 window[vendors[x] + ‘CancelRequestAnimationFrame‘]; 11 } 12 13 if (!window.requestAnimationFrame) { 14 window.requestAnimationFrame = function(callback) { 15 var currTime = new Date().getTime(); 16 var timeToCall = Math.max(0, 16.7 - (currTime - lastTime)); 17 var id = window.setTimeout(function() { 18 callback(currTime + timeToCall); 19 }, timeToCall); 20 lastTime = currTime + timeToCall; 21 return id; 22 }; 23 } 24 if (!window.cancelAnimationFrame) { 25 window.cancelAnimationFrame = function(id) { 26 clearTimeout(id); 27 }; 28 } 29 }());
3.2、项目中的减法
这种要做减法的情况下,最佳的体验是自己把动画都完成好,直接演示给产品、设计童鞋看,他们就能从中取舍了,没有任何的扯皮、分歧。tips:当然动画不需要跟实际用的一致,几个特性差不多就好,比如主动画背景图非常大,多个动画并行,且持续时间比较长。
3.3、关于大图动画
这种动画渲染的性能消耗往往非常大,考虑开启GPU来加速图层的渲染,但是由于GPU的渲染又是非常耗费内存、电量的,所以在不需要移动的情况下,需要关闭GPU,防止浏览器的崩溃。在该项目中,当用户进行游戏交互等,触发主动画暂停时,将通过设置translate2d,取消加速;启动时,再开启。
3.4、按需加载/卸载动画
对于场景动画来说,有些动效,只有出现在视口时,才有播放的必要性;离开视口,就可以关闭。通过判断动画当前的位置,可以实现动画的按需加载。
3.5、重绘、回流
动画帧内减少处理,即避免长时间的js执行;减少回流、重绘在哪里都适用,可以用transform等操作,来替代position;left等等的操作。当需要display:none的情况下(回流),使用opacity:0(重绘);或者visibility:hidden(重绘),将更优。回流的性能消耗要远大于重绘。
3.6、首屏优先加载
在网速很可观的情况下,完全可以同时加载整站的资源,仅将首屏的资源前置即可。
但在网络状况很抓狂的情况下(如3g下),这种大量图片、音频的页面,就需要考虑资源分批启动加载的必要性了。在网速欠佳,但是服务器允许多并发的情况下,同时可以请求多个资源,此时带宽及其有限的,虽然整体的加载时间没变化,但是首屏的加载时间却延长了。如当前带宽时750kb/s,10个资源一起请求,则每个分到75kb/s,150kb的图片,需要加载2s;如果只有3个资源一起请求,分到250kb/s,需加载0.6s。因此,为了体验更佳,首屏的资源加载完毕,再开启主动画资源加载。
3.7、资源的预加载
因为主动画中的交互、资源较多,需要资源稳定以后才能有更好的用户体验,所以这里提供了资源的预加载。
图片预加载:
newImgObjs[i] = new Image(); newImgObjs[i].src = animationImgs[i]; newImgObjs[i].onload = function() { loadeds++; if (loadeds == newImgObjs.length) { self.barObj.completeWelcomePromise.resolve(); console.log(‘图片资源已经加载完成‘); } }; newImgObjs[i].onerror = function() { loadeds++; if (loadeds == newImgObjs.length) { self.barObj.completeWelcomePromise.resolve(); console.log(‘图片资源已经加载完成‘); } };
$audio[0].src = imgPath; $audio.on(‘canplaythrough‘, function() { loadeds++; if (loadeds == (newImgObjs.length + 1)) { self.barObj.completePromise.resolve(); console.log(‘音频资源已经加载完成‘); } }); $audio[0].onerror = function(e) { loadeds++; if (loadeds == (newImgObjs.length + 1)) { self.barObj.completePromise.resolve(); console.log(‘音频资源已经加载完成‘); } };
3.8、其他几种
限频、dom操作、雪碧图等不再多说。关于响应式下的雪碧图处理,上一篇博客有提供系统的解决方案。
3.9、promise的使用
在该项目中,promise的使用较为频繁,包括ajax请求、图片的预加载、进度条的处理等。
// 资源处理:预加载、监控加载完成、渲染 $.when(self.cmsInfoPromise, self.goodsInfoPromise, self.barObj.completeWelcomePromise) .done(function() { // 进度条动效 self.barAnimation(); self.handleResource(); self.renderStores(); });
以上是关于动画性能优化-requestanimationframeGPU等的主要内容,如果未能解决你的问题,请参考以下文章