首頁  >  文章  >  web前端  >  CSS vs JS動畫:誰比較快?

CSS vs JS動畫:誰比較快?

大家讲道理
大家讲道理原創
2016-11-10 11:53:141132瀏覽

 這篇文章翻譯自 Julian Shapiro 的 CSS vs. JS Animation: Which is Faster?。 Julian Shapiro 也是 Velocity.js 的創造者。這是一個非常有效率、簡單好用的JS動畫庫。他在Web動畫方面有很高的造詣。

                                可能總是和 CSS transition 一樣快,甚至更快呢?到底是什麼秘密呢? Adobe 和 Google 是怎麼做到讓他們的富媒體行動網站的速度和 native app 媲美的? CSS vs JS動畫:誰比較快?

這篇文章會一步步告訴你為什麼基於 Javascript 的 DOM 動畫庫(例如 Velocity.js 和 GSAP)能夠比 jQuery 和基於 CSS 的動畫庫更有效率。

 jQuery

  讓我們從基本開始說起: Javascript 和 jQuery 兩者不能混為一談。 Javascript 動畫很快,而 jQuery 動畫很慢。為什麼呢?因為儘管jQuery 異常強大,但是它的設計目標並不是一個高效的動畫引擎:

jQuery 不能避免layout thrashing (有人喜歡將其翻譯為“佈局顛簸”,會導致多餘relayout/reflow),因為它的程式碼不僅用於動畫,它還用於許多其他場景。
  • jQuery的記憶體消耗較大,常會引發垃圾回收。而垃圾回收觸發時很容易讓動畫卡住。
  • jQuery使用了setInterval而不是 reqeustAnimationFrame(RAF),因為 RAF 會在視窗失去焦點時停止觸發,這會導致jQuery的bug。 (目前jQuery已經使用了RAF)
  • 注意 layout thrashing 會導致動畫在開始的時候卡頓,垃圾回收的觸發會導致動畫運行過程中的卡頓,不使用 RAF 則會導致動畫幀率低。
實作範例

為了避免layout thrashing,我們需要大量存取和更新DOM。

var currentTop,
    currentLeft;
/* 有 layout thrashing. */
currentTop = element.style.top; /* 访问 */
element.style.top = currentTop + 1; /* 更新 */
currentLeft = element.style.left; /* 访问 */
element.style.left = currentLeft + 1; /* 更新 */
/* 没有 layout thrashing. */
currentTop = element.style.top; /* 访问 */
currentLeft = element.style.left; /* 访问 */
element.style.top = currentTop + 1; /* 更新 */
element.style.left = currentLeft + 1; /* 更新 */

  在更新操作之後的存取操作會強制瀏覽器重新計算頁面元素的樣式(因為要將更新的樣式套用上去才能取得正確的值)。這在一般操作下沒多大的效能損失,但是放在間隔僅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 Transition

  CSS transition 的動畫邏輯是由瀏覽器來執行,所以它的性能能夠比 jQuery 動畫好。它的優勢體現在:

透過最佳化DOM 操作,避免記憶體消耗來減少卡頓
  • 使用與RAF 類似的機制
  • 然而實際上Javascript也可以使用這些優化。 GSAP 做這些優化很久了。 Velocity.js 是一個新興的動畫引擎,它不僅做了這些優化,甚至走的更遠。我們稍後會談到這些。
  面對事實,讓 Javascript 動畫得以媲美 CSS 動畫的性能只是我們偉大計劃的第一步。第二步才是重頭戲,要讓 Javascript 動畫比 CSS 動畫還要快!

  讓我們來看看 CSS 動畫庫的缺陷吧:

Transition 強制使用了 GPU 的硬體加速。導致瀏覽器一直處於高負載運轉的狀態,反而會讓動畫變的卡頓。這在行動瀏覽器上更為嚴重。 (特別要說明的是,當資料在瀏覽器的主線程和合成線程之間頻繁傳輸的時候特別消耗效能,故容易導致卡頓。某些 CSS 屬性,不會受到影響。 Adobe 的部落格談到這個問題。

  • IE 10以下的瀏覽器不支援 transition。而目前 IE8 和 IE9 還是很流行的。

  • transition 不能完全被 Javascript 控制(只能透過 Javascript 觸發 transition),因為瀏覽器不知道如何同時讓 Javascript 控制動畫又同時最佳化動畫的效能。

  •   反過來說: 基於 Javascript 可以決定什麼時候啟用硬體加速,它可以支援全版本的 IE,並且它完全可以進行批量動畫的最佳化。

  • 我的建议是:当你只在移动平台上开发,并且动画只是简单的状态切换,那么适合用纯 CSS transition。在这种情况下,transition 
    是高性能的原生支持方案。它可以让你将动画逻辑放在样式文件里面,而不会让你的页面充斥 Javascript 
    库。然而如果你在设计很复杂的富客户端界面或者在开发一个有着复杂UI状态的 
    app。那么我推荐你使用一个动画库,这样你的动画可以保持高效,并且你的工作流也更可控。有一个特别的库做的特别棒,它可以用 Javascript 
    控制 CSS transition。这就是 Transit。
  • Javascript 動畫

  •   所以 Javascript 可以比 CSS transition 性能更好。但是它到底有多块呢?它快到足够可以构建一个3D 动画的demo,通常需要用到 WebGL 才能完成。并且它快到足够搭建一个多媒体小动画,通常需要 Flash 或者 After Effects 才能完成。并且它还快到可以构建一个虚拟世界,通常需要 canvas 才能完成。

      为了更直接的来比较主流动画库的性能,包括 Transit(使用了 CSS transition),让我们打开Velocity的官方文档。

      之前那个问题还在:Javascript 是如何达到高性能的呢?下面是一个列表,列举了基于 Javascript 的动画库能做的事情:

    • 同步DOM -> 在整个动画链中微调堆栈以达到最小的layout thrashing。

    • 缓存链式操作中的属性值,这样可以最小化DOM的查询操作(这就是高性能 DOM 动画的阿喀琉斯之踵)

    • 在同一个跨同层元素的调用中缓存单位转化比率(例如px转换成%、em等等单位)

    • 忽略那些变动小到根本看不出来的DOM更新

      让我们重新温习下之前学到的关于layout thrashing的知识点。Velocity.js 运用了这些最佳实践,缓存了动画结束时的属性值,在紧接的下一次动画开始时使用。这样可以避免重新查询动画的起始属性值。

    $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%。

      浏览器也可以使用与此类似的优化,但是要做这些事情太过激进,使用场景也会受到限制,开发者就有可能会写出有bug的动画代码。jQuery就是因为这个原因没有使用RAF(如上所说),浏览器永远不会强行实施可能打破规范或者可能偏离期望行为的优化。

      最后,让我们来比较下两个Javascript框架(velocity.js 和 GSAP)。

    • GASP 是一个快速且功能丰富的动画平台。Velocity则更为轻量级,它大大地改善了UI动画性能和工作流程。

    • GSAP 需要付费才能用于商业产品。Velocity 是完全免费的,它使用了自由度极高的 MIT 协议。

    • 性能方面,两者几乎相当,很难区分胜负。

    我个人推荐在你需要如下功能时使用 GSAP:精确控制时间(例如 remapping,暂停/继续/跳过),或者需要动作(例如:贝赛尔曲线路径),又或者复杂的动画组合/队列。这些特性对游戏开发或者复杂的应用很重要,但是对普通的 web app 的 UI 不太需要。

     Velocity.js

      之前提到了 GSAP 有着丰富的功能,但这不代表 Velocity 的功能简单。相反的,Velocity 在 zip 压缩之后只有 7kb,它不仅仅实现了 jQuery animate 方法的所有功能,还包含了 颜色、transforms、loop、easings、class 动画和滚动动画等功能。

      简单的说就是 Velocity 包含了 jQuery、 jQuery UI 和 CSS transition 的功能。

      更进一步从易用性的角度来讲,Velocity 使用了 jQuery 的$.queue() 方法,因此可以无缝过渡到 jQuery 的$.animate()、$.fade()和$.delay()方法。并且 Velocity 的语法和$.animate()一摸一样,所以我们根本不需要修改页面的现有代码。

      让我们快速过一下 Velocity.js 的例子:

    $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);

      如下是一个高级用法:滚动网页到当前元素并且旋转元素。这样的动画只需要简单的几行代码:

    $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 动画领域性能最好易用性最高的库。这篇文章主要关注了性能方面。易用性方面可以前往 VelocityJS.org 了解。

      在结束之前,请记住一个高性能的 UI 绝不仅仅是选择一个正确的动画库。页面上的其他代码也需要优化。可以看看Google那些非常棒的演讲:

    Jank Free

    Rendering Without Lumps

    Faster Websites


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn