動畫性能優化-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  
     它是一種高級的方法,存在兼容性問題。主要運作方式是瀏覽器要進行繪制的時候(一般16.7ms一次繪制),會通知requestAnimationFrame們,requestAnimationFrame們就跟它一起繪制。這里有幾個好處,多個requestAnimationFrame可以同時進行,而setTimeout需要獨立繪制;頁面切換等情況,瀏覽器不再繪制該頁面,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操作、雪碧圖等不再多說。關於響應式下的雪碧圖處理, 上一篇博客有提供系統的解決方案。
 
4、promise的使用  
     在該項目中,promise的使用較為頻繁,包括ajax請求、圖片的預加載、進度條的處理等。
    
// 資源處理:預加載、監控加載完成、渲染
$.when(self.cmsInfoPromise, self.goodsInfoPromise, self.barObj.completeWelcomePromise)
    .done(function() {
        // 進度條動效
        self.barAnimation();
        self.handleResource();
        self.renderStores();
     });    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM