JavaScript/jQuery動畫與CSS動畫性能比較
一直以來,大家可能認為基於JavaScript的動畫總是比CSS動畫一樣快或更快。事實是這樣的嗎?本文將介紹基於JavaScript的DOM動畫庫(如Velocity.js和GSAP)是否比基於jQuery和CSS的動畫庫更為出色。
jQuery
JavaScript和jQuery被錯誤地混為一談。JavaScript動畫很快。jQuery減慢了速度。為什麼?因為 - 儘管jQuery非常強大 ,但jQuery的設計目標並不是成為一個高性能的動畫引擎:
•jQuery不能避免佈局的擺動(layout thrashing),因為它的代碼庫,為的是除了動畫以外的許多用途。
•jQuery的內存消耗頻繁地觸發了暫時凍結動畫的垃圾收集。
•jQuery使用setInterval而不是requestAnimationFrame(RAF)
需要注意的是佈局中造成啟動動畫抖動,垃圾收集時導致動畫的抖動,其根本原因就是產生較低的幀速率。
實現的例子
避免佈局抖動包括簡單地將DOM查詢和DOM更新組合在一起:
var currentTop,currentLeft;
/* With layout thrashing. */
currentTop = element.style.top; /* QUERY */
element.style.top = currentTop + 1; /* UPDATE */
currentLeft = element.style.left; /* QUERY */
element.style.left = currentLeft + 1; /* UPDATE */
/* Without layout thrashing. */
currentTop = element.style.top; /* QUERY */
currentLeft = element.style.left; /* QUERY */
element.style.top = currentTop + 1; /* UPDATE */
element.style.left = currentLeft + 1; /* UPDATE */
在更新後進行的查詢強制瀏覽器重新計算頁面的樣式數據(同時考慮新的更新效果)。這對於僅在16ms的微小間隔上運行的動畫產生顯著的開銷。
同樣,執行RAF也不需要對現有代碼庫進行重大的修改。我們來比較RAF的基本實現與setInterval的基本實現:
var startingTop = 0;
/* setInterval: Runs every 16ms to achieve 60fps (1000ms/60 ~= 16ms). */
setInterval(function() {
/* Since this ticks 60 times a second, we divide the top property's increment of 1 unit per 1 second by 60. */
element.style.top = (startingTop += 1/60);
}, 16);
/* requestAnimationFrame: Attempts to run at 60fps based on whether the browser is in an optimal state. */
function tick () {
element.style.top = (startingTop += 1/60);
}
window.requestAnimationFrame(tick);
RAF可以通過對代碼的單一更改,為動畫效果帶來最大的提升。
CSS Transitions
通過將動畫邏輯轉載到瀏覽器本身,CSS Transitions優於jQuery,這是有效的:
1)優化DOM交互和內存消耗以避免抖動
2)利用RAF的原理
3)強制硬件加速(利用GPU提升動畫效果)
然而,現實情況是,這些優化也可以直接在JavaScript中執行。GSAP已經做了很多年了;Velocity.js,一個新的動畫引擎,同樣是利用這樣的技術。
根據JavaScript動畫可以更優於CSS動畫的事實,那我們首先來看看CSS動畫庫的缺點:
Transitions強制硬件加速GPU,導致高壓力情況下的抖動和綁定。這些影響在移動設備上惡化。(具體而言,抖動是當數據在瀏覽器的主線程和其合成的線程之間傳遞時發生較大開銷的結果。一些CSS屬性,如transforms 和opacity,就是如此。)
transforms在Internet Explorer 10以下無法工作,導致桌面版網站的輔助功能問題,因為IE8和IE9仍然還有較大量的用戶。
因為transforms不被JavaScript本地控制(他們只是引發JavaScript),瀏覽器不知道如何優化同步的JavaScript代碼,操縱他們的轉換。
相反:基於JavaScript的動畫庫可以自己決定什麼時候啟用硬件加速,它們固有地在所有版本的IE中工作,它們非常適合批量動畫優化。
JavaScript動畫
當涉及到性能時,JavaScript佔據上風。但是JavaScript能有多快呢?現在,來建立一個3D動畫演示(可以訪問:http://www.ikinsoft.com/3ddemo/Velocity.html)。
問題仍然存在:JavaScript如何達到高水平的性能?以下是基於JavaScript的動畫能夠執行的優化列表:
在整個動畫鏈中同步DOM→補間堆棧,以最大限度地減少佈局抖動。
通過鏈接調用來緩存屬性值,以便最小化DOM查詢的發生(這是執行DOM動畫的基本技能)。
同一個調用中的同級單元的緩存單元轉換率(例如,px到%,em等)。
當視覺上看不到更新時,跳過樣式更新。
Velocity.js利用這些實踐來緩存要重複使用的動畫的最終值,作為隨後的動畫的起始值,從而避免重新使用元素的起始值的DOM:
$element
/* Slide the element down into view. */
.velocity({ opacity: 1, top: "50%" })
/* After a delay of 1000ms, slide the element out of view. */
.velocity({ opacity: 0, top: "-50%" }, { delay: 1000 });
在上面的例子中,第二個Velocity調用知道它opacity值為1,最高值為50%並自動開始運行。
瀏覽器最終可以執行許多這些相同的優化,但是這樣做將會大大縮小開發者製作動畫代碼的方式。因此,由於同樣的原因,jQuery不使用RAF,因而瀏覽器永遠不會進行優化,甚至有可能破壞規範或偏離預期行為的機會。
總結
本文對比了JS及其框架和CSS3的動畫性能,並深入剖析了其內在原因。技術結論大致如下:
jQuery出於設計原因,在動畫性能上表現最差
CSS3由於把動畫邏輯推給了瀏覽器,優化了內存消耗、DOM操作和默認利用了RAF,所以要比jQuery動畫性能更好
CSS3可能會引起瀏覽器主線程和複合器線程之間過度數據交互,從而導致性能下降
純JS實現的動畫,在利用RAF和注意佈局擺動處理時,可以獲得媲美CSS3的動畫性能,而在瀏覽器兼容性上比CSS3更好
應用選型建議:
對於簡單頁面動畫,建議優先選擇CSS3動畫
對於複雜動畫,建議使用GSAP或Velocity