CSS 和 JS 動畫哪個更快

oschina發表於2014-05-02

  基於Javascript的動畫暗中同CSS過渡效果一樣,甚至更加快,這怎麼可能呢?而Adobe和Google持續釋出的富媒體移動網站的效能可媲美本地應用,這又怎麼可能呢?

  本文逐一遍覽了基於Javascript的DOM動畫庫,如Velocity.js和GSAP,看其是如何比jQuery和CSS動畫效果更具效能的.

 jQuery

  讓我們先從基礎的開始: JavaScript 和 jQuery 被錯誤的混為一談了. JavaScript 動畫是很快的. jQuery 把它放慢了下來。為什麼?因為 — 儘管jQuery非常強大 — 但成為一個效能強勁的動畫引擎從來都不是jQuery的設計目標:

  應該注意到佈局顛簸就是在動畫開始部分的不順暢,垃圾回收就是造成動畫期間不順暢的元凶, 而沒有使用RAF則會導致低幀率.

 實現示例

  避免造成佈局顛簸的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 */

  發生在更新之後的查詢會強制瀏覽器對頁面的計算式資料進行重新計算 (同時會把新的更新效果考慮在內). 這樣就會對動畫產生顯著的開銷,而這只是16毫秒微小間隔的執行超時.

  類似的,實現 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 轉換

  CSS轉換通過把動畫邏輯甩給瀏覽器本身去處理而超越了jQuery,這在以下幾方面是有效果的:(1)優化DOM互動和記憶體消耗以避免卡頓(顛簸),(2)利用引擎的RAF原則,(3)強制硬體加速(利用GPU的能力來提高動畫效能)。

  然而,現實是,這些優化也可以在JavaScript中直接執行。GSAP已經這樣做了多年。Velocity.js,一個新的動畫引擎,不僅利用了同樣的技術,而且還向前多走了幾步——我們不久會探討這些。

  面對事實,JavaScript動畫可以與CSS轉換競爭只是我們康復計劃的第一步。第二步是實現“JavaScript動畫實際上可以比CSS轉換更快”。

  現在我們開始談談CSS變換的弱點:

  • transition強制硬體加速會加大GPU消耗,高負荷情形下將導致執行不流暢。這種情況在移動裝置上尤為明顯。(特殊情況下,比如當資料在瀏覽器主執行緒和排版執行緒之間傳遞產生的瓶頸也會導致不流暢)。某些CSS屬性,比如transform和opacity,則不受這些瓶頸影響。Adobe在這裡精心總結了這些問題。

  • transition在IE10以下沒有用,造成的自IE8和IE9以來的桌面站點可用性問題至今仍然廣泛存在。

  • 由於transition並不是由JavaScript原生控制(而僅僅是由JavaScript觸發),瀏覽器無法獲知如何與控制這些transition的JavaScript程式碼同步地優化他們。

  相反的,基於JavaScript的動畫庫則可以自行確定合適開啟硬體。它們原生支援各版本IE瀏覽器,並且它們尤其適合批量動畫優化。

  我的建議是僅當你單獨為移動端開發且僅實現簡單動畫時使用原生CSS變換。這種環境下,transition是一種原生有效的解決方案,可以使你在樣式表中實現所有動畫邏輯,而不用新增額外的JavaScript庫,從而避免你的頁面變得臃腫。然而,但你在設計複雜的UI,或者是開發存在不同狀態的UI的App時,你就應該使用動畫庫以使動畫保持流暢,同時是工作流程易於管理。Transit是一個在管理CSS變換方面做得尤其優秀的庫。

 JavaScript 動畫

  好了,那JavaScript可就在效能方面佔據上風了. 但Javascript究竟具體快了多少呢? 好吧 — 最初 — 對於構建一個實在的 3D動畫示例 是足夠快的,通常在構建中你只會看到有使用WebGL. 而構建一個 多媒體小動畫 也夠了,通常你看到只會使用Flash或者After Effects構建. 而構建一個 虛擬世界 也夠了,通常你只會看到使用canvas構建.

  為了對領先的動畫庫,當然還要包含Transit(它使用CSS漸變效果),進行直接的對比, 回頭去看看Velocity在VelocityJS.org上的文件.

  問題仍然是: 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 和 top為50% 開始。

  瀏覽器本身最終能夠執行許多這些相同的優化,但這樣做會明顯減少開發者能夠製作的動畫程式碼的方式。因此,出於同樣原因,由於jQuery不使用RAF(如上所述),瀏覽器就不會強制優化它,甚至給出一個很小的機會去打破規格或偏離預期的行為。

  最後,我們對這兩個JavaScript動畫庫(Velocity.js 和 GSAP)互相比較一下。

  GSAP was the first animation library to demonstrate JavaScript's impressive DOM animation performance. It does, however, have a few drawbacks:

  • In medium-to-high stress animations, GSAP's DOM interaction overhead causes dropped frames at the start of, and between, animations.

  • Whereas Velocity.js is released under the ultra-permissive MIT license, GSAP is closed-source and requires an annual licensing fee for many types of businesses.

  • Because GSAP is a full animation suite, it is triple the size of Velocity. However, since GSAP is so richly-featured, it does have the benefit of being a Swiss Army Knife of animation.

  My recommendation is to use GSAP when you require precise control over timing (e.g. remapping, pause/resume) and motion (e.g. bezier curve paths). These features are crucial for game development and certain niche applications, but are typically not needed in web app UI's.

 Velocity.js

  引用 GSAP 豐富的特性並不代表Velocity自身在特性上是輕量級的. 相反,在壓縮後僅有的7kb中,Velocity不僅僅複製了jQuery $.animate()的所有功能, 它還把顏色動畫,轉換,迴圈,easing效果,類動畫還有滾動都打包了進去.

  總之,Velocity是jQuery,jQuery UI,以及CSS漸變效果的最佳組合.

  此外,從便利的角度看,Velocity在hood(蓋子,大概意思是公共的介面)之下使用jQuery的 $.queue() 方法, 如此就可以實現同 jQuery 的 $.animate(), $.fade(), 和 $.delay() 函式的無縫互操作. 而且,由於Velocity的語法同 $.animate() 的語法是相同的, 你不需要改變頁面的任何程式碼.

  讓我們快速地來看一看 Velocity.js. 在基礎的層面,Velocity的行為同$.animate()一樣:

$element
    .delay(1000)
    /* Use Velocity to animate the element's top property over a duration of 2000ms. */
    .velocity({ top: "50%" }, 2000)
    /* Use a standard jQuery method to fade the element out once Velocity is done animating top. */
    .fadeOut(1000);

  在其最高階的層面,可以建立帶有3D動畫的複雜滾動場景 — 幾乎只要用到兩行簡單的程式碼:

$element
    /* Scroll the browser to the top of this element over a duration of 1000ms. */
    .velocity("scroll", 1000)
    /* Then rotate the element around its Y axis by 360 degrees. */
    .velocity({ rotateY: "360deg" }, 1000);

 結束

  Velocity的目標是仍舊成為 DOM 中的動畫效能和便利性的領導者。本文側重於前者。  Head on over to  VelocityJS.org更多地瞭解後者。

  之前,我們得出結論,記住,一個高效能的UI並不僅僅是選擇更多的Lib。你的頁面的其餘部分也應進行優化。可以從Google talks瞭解更多,更夢幻的內容:

  原文地址:http://davidwalsh.name/css-js-animation

相關文章