首页 > 代码库 > 动画性能优化-requestanimationframe、GPU等

动画性能优化-requestanimationframe、GPU等

  最近在做一个场景动画,有一个欢迎界面和一个主动画界面,两个界面之间的连接通过一个进度条来完成,当进度条完成,提供通往主动画的按钮。
  画面会从一个个的场景移动过去,用户可通过点击抽奖、查看气泡商铺等进行交互,同时可拖动画面,前移或后退。该项目中,出了主动画,还有人物场景对话的动画等,性能的优化、用户的体验变得尤为重要,这里总结一下在开发过程中使用的一些性能、体验优化方法。 
 
1、动画
  a、优先采用requestanimationframe,实现动画帧的并发渲染;
  b、做减法:兼容低版本浏览器(a中的元素不生效,通过setTimeout实现动画),保留主动画性能,去除重要性不大的动画(跑马灯、过程小动画等);
  c、大图动画性能消耗非常大,使用translate3d实现GPU加速,动画结束、暂停时,切换回2d,取消加速;
  d、按需加载/卸载动画;
  e、每个动画帧处理函数简化,尽量减少或者去除回流、重绘。
 
2、加载、用户体验优化
  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 =http://www.mamicode.com/ 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();
     });    

 

动画性能优化-requestanimationframe、GPU等