本文是求索:GSAP的動畫快於jQuery嗎?爲什麼? 的續文。GSAP是一個js動畫插件,它聲稱「20x faster than jQuery」,是什麼讓它這麼快呢?javascript
每當有這樣的問題的時候,咱們能夠經過如下步驟來肯定一個未知的解決方案的性能優化是怎麼
作到
/僞造
的:css
- 黑盒:從官方用例來看,究竟有多快,快在哪兒
- 白盒:看看官方用例以內,框架怎麼作到優化的
- do { 提出假設,本身構建用例測試 } while (假設沒有獲得驗證);
- 得出結論
本文是整個探究過程的後兩個部分。html
本文可能須要你知道的一些知識前端
requestAnimationFrame相關:java
瀏覽器工做原理node
瀏覽器提供的調試工具jquery
注:這裏沒有更好的inspector快照內容介紹(翻譯的,或是足夠詳盡的),若是你知道的話不妨告訴我喲~git
爲了下文閱讀方便,這裏從profiler獲得的結果角度,提一下瀏覽器裏,從定時器被激活,到用戶看到圖形的主要流程:github
CHROME
腳本運行(黃色)->迴流與重計算(紫色)->重繪(綠色)->其餘(白色,探查器的相關內容)ajax
IE11
加載(深藍色)->腳本運行(紅色)->內存垃圾回收(黃色)->迴流與重計算(綠色)->重繪(紫色)->其餘(灰色,探查器的相關內容)
注意到chrome和IE11的重繪與迴流/重計算的表示顏色是相反的。
下文中的測試,chrome的計時區間是一個gc的間隔(50s左右)(由於在chrome裏,FPS和內存佔用有比較小的反相關,內存佔用越多,FPS越低),而IE則採用10s的間隔(由於在IE 11裏,FPS和內存佔用看不出相關性)。
同時,不對不支持requestAnimationFrame的瀏覽器作出評測。
注:這裏也沒有測試firefox,由於目前尚未找到靠譜的測量UI響應的功能,若是有請@我,多謝
最後,爲了下文比較方便,咱們在chrome和IE11下再次測試官方給的用例,得到表格(單位FPS,爲平均值,後面均同樣,再也不次說明):
瀏覽器 | chrome | IE 11 |
GSAP test: jQuery | 14 | 22 |
GSAP test: GSAP | 45 | 53 |
根據前文所述,咱們獲得如下假設:
setInterval
,受到瀏覽器重繪上限的控制,而GSAP採用requestAnimationFrame
,徹底將重繪交給瀏覽器管理,以得到更好地重繪性能是這樣的嗎?
以前已經有這樣的測試了:《setInterval與requestAnimationFrame的時間間隔測試》,但有如下弊病:
<style> body{ position:relative; height:100% ; background:#000; overflow:hidden; } div{ width : 4px; height : 4px; border-radius : 100%; position : absolute; } </style>
在body內部填入N(N=500,能夠改變這個值)個div,設置它們的圓角、背景屬性。而後在每幀裏面隨機改變他們的top
、left
(對應着迴流)、transform
(對應着重繪)值,這樣,可讓每幀的繪製裏頁面常見的繪製操做耗時均等。大體就是要繪製如下的效果。
測試代碼以下:
http://jsfiddle.net/humphry/HNysW/4/
CHROME結果:
setTimeout(fn,10)
setTimeout(fn,13)
setTimeout(fn,16)
requestAnimationFrame
setInterval(fn,16)
- 「我去,幀率沒有太大區別嘛。」
- 「該不是由於咱們沒有測IE嘛?」
恩,那麼咱們進入IE11,。下圖是requestAnimationFrame的測試結果,惋惜沒有統計,也不支持導出數據,不能知道平均幀率。
我將setInterval的結果印在requestAnimationFrame上,二者的對比可見下圖:
- 「並無沒有顯著的提高啊。」
- 「看起來必定是咱們沒有夠數據量的緣故。」
來吧,調節點的數量:
咱們如今放進去1000個小點,咱們在IE11下的結果對比如下:
- 「要說有那麼一點優化,倒是一點優化都看不出來的感受呢。」
- 「看起來必定是咱們沒有測小繪製壓力的緣故。」
或者調節成200個小點呢,咱們在IE11下幀率對比一下:
再綜合一下數據:
小點個數 | 200 | 500 | 1000 | |||
瀏覽器 | chrome | IE 11 | chrome | IE 11 | chrome | IE 11 |
requestAnimationFrame | 53 | 60 | 30 | 32 | 17 | 19 |
setInterval 16 | 53 | 60 | 29 | 30 | 12 | 19 |
- 「爲何,爲何看不出來效果呀 擦 擦」
- 「阿瑪,咱們不是被涮了吧!」
- 「不可能!他們說得真真兒的,到底是什麼地方不對呢……咱們是否是把迴流和重繪弄得太均勻了啊」
……好吧,那麼我構造一個這樣的文檔結構,div
套div
套div
套div
套……讓它們都爲inline-block
,以得到包裹的效果……
body{ height:100% ; background:#000; overflow:hidden; } html{ height: 100%; } div{ padding: 1px 3px 2px 0; border-top: 2px solid; display: inline-block; }
大概像是這樣。
這個時候改變最裏面的div的寬度,就會致使大面積的迴流了。
http://jsfiddle.net/humphry/SwLY7/1/
能夠看到,佔大比例的是迴流(紫色部分)。hover到layout處,能夠看到:
全員參與迴流,CPU什麼的必定很帶感呢。
結果:
參與嵌套的DIV數量 (reflow) | 200 | 500 | 700 | 1000 | ||||
瀏覽器 | chrome | IE 11 | chrome | IE 11 | chrome | IE 11 | chrome | IE 11 |
requestAnimationFrame | 48 | 60 | 26 | 60 | 15 | 60 | 8 | (崩潰) |
setInterval 16 | 47 | 60 | 26 | 60 | 15 | 60 | 8 | (崩潰) |
咱們來設置陰影,以期得到較長的重繪時間:
一閃一閃亮晶晶~
http://jsfiddle.net/humphry/XdFqR/1/
如圖,綠色的重繪佔據了大部分。
結果:
閃爍的星星數量(repaint) | 80 | 100 | 200 | |||
瀏覽器 | chrome | IE 11 | chrome | IE 11 | chrome | IE 11 |
requestAnimationFrame | 54 | 16 | 45 | 12 | 26 | 4 |
setInterval 16 | 47 | 16 | 38 | 10 | 24 | 5 |
- 「……」
- 「……儘管在chrome下是有一些提高,可是也沒有達到用例那麼明顯,達到兩倍的關係呀阿瑪。」
咱們來再回顧一次結果:
咱們前面的三個測試:
基本上能夠說明,在低渲染壓力/中等渲染壓力/高渲染壓力三種場景中,requestAnimationFrame
裏面的重繪性能平均比setInterval
快1~5幀左右,而回流性能則沒有很大影響。
那麼,GSAP是如何讓用例出現這麼大的區別呢?chrome裏快2倍,IE11裏面快1倍,這不是一個輕易換用requestAnimationFrame就能夠達到的結果。
我想到,setInterval這個函數其實並非一次觸發,而是針對每一個DOM觸發的。在此以前的全部測試,皆是集中的計時器。所以,有了第四個測試:
第四個測試,就是將前面的第一個和第三個測試改一下,將集中改爲分散。方案是,擴展HTMLDivElement原型方法,而後讓每一個DOM調用它,便可模擬在官方的jQuery測試用例中發生的事情。
HTMLDivElement.prototype.startAnimationOnMyOwn = function() { var that = this ; setInterval(function(){ repaint(that) ; } , 16) ; } ; for (var i = 0; i < NUM; i++) { allnodes[i].startAnimationOnMyOwn() ; }
測試1的去中心化版:
http://jsfiddle.net/humphry/pBwBx/
測試3的去中心化版:
http://jsfiddle.net/humphry/7fa64/
獲得結果:
測試1·改(去中心化的重繪/requestAnimationFrame)
圓點數量 | 200 | 500 | 1000 | |||
瀏覽器 | chrome | IE 11 | chrome | IE 11 | chrome | IE 11 |
rAF (集中) | 53 | 60 | 30 | 32 | 17 | 19 |
rAF (去中心化) | 17 | 20 | 12 | 2 | 6 | 1 |
setInterval 16 (集中) | 53 | 60 | 29 | 30 | 12 | 19 |
setInterval 16 (去中心化) | 19 | 34 | 8 | 15 | 4 | 6 |
測試3·改(去中心化的重繪/requestAnimationFrame)
閃爍的陰影數量 | 80 | 100 | 200 | |||
瀏覽器 | chrome | IE 11 | chrome | IE 11 | chrome | IE 11 |
rAF (集中) | 54 | 16 | 45 | 12 | 26 | 4 |
rAF (去中心化) | 33 | 13 | 27 | 12 | 16 | 5 |
setInterval 16 (集中) | 47 | 16 | 38 | 10 | 24 | 5 |
setInterval 16 (去中心化) | 36 | 14 | 31 | 10 | 14 | 5 |
這個表中終於出現了很是大的數據波動,也符合GSAP的的測試結果,咱們能夠得出結論了。
比較直觀的全部結果比較:
setInterval是否比requestAnimationFrame更慢?
不是的。上表中的數據能夠代表這一點,在短的回調裏,形成重繪的相關代碼,requestAnimationFrame比setInterval稍微快一些;可是在長回調函數(屢次構造requestAnimationFrame,它們會被合到一個裏面)裏,requestAnimationFrame不比setInterval快。
這也是《理解WebKit:渲染主循環main loop和rAF》裏說:「回調函數不能太大,不能佔用太長時間,不然會影響頁面的響應和繪製的頻率」的緣由。
requestAnimationFrame最主要的意義,是降幀而非升幀,以防止丟幀。它的目的更相似於垂直同步,而非越快越好。
MSDN: 幀率不等或跳幀會令人感受你的站點速度緩慢。若是下降動畫速度能夠減小跳幀並有助於保持幀率一致,它可使人感受站點速度更快。
閱讀更多:http://creativejs.com/resources/requestanimationframe/
集中定時器形成重繪是否比分散定時器形成重繪快?
是的。這也是GSAP更快的緣由。
測試須要改進嗎?
我認爲須要。測試過程當中爲了比較好的效果用了隨機數。其實生成隨機數的過程當中也耗費了必定的時間,更好的測試中,能夠用線性的變化替換隨機離散的數值變化,數據會更加穩定。
同時,沒有測試firefox,很惋惜,到目前爲止,筆者依然沒有找到一款好使的分析UI的插件。
GSAP的用例是否說明了GSAP快於jQuery呢?
是的,這能夠說明GSAP更快。但並不是是在任什麼時候候都更快。在咱們須要粒子系統時快,在咱們只須要繪製一兩個小交互時,它沒有提供很是明顯的性能優化的可能性。後者能夠直接用CSS3 Animation/Translation作,或者用jQuery達到全瀏覽器兼容。若出現了粒子系統這樣的大量元素重繪的需求,用GSAP是很好的選擇。
形成GSAP更快的緣由,是因爲jQuery的處理方式,很是不適合繪製大量節點。
再次摘抄用例:
tests.jquery = { tween:function(dot) { dot[0].style.cssText = startingCSS; var angle = Math.random() * Math.PI * 2; dot.delay(Math.random() * duration).animate({left:Math.cos(angle) * radius + centerX, top:Math.sin(angle) * radius + centerY, width:32, height:32}, duration, "cubicIn", function() { tests.jquery.tween(dot) }); } }; function toggleTest() { i = dots.length; while (--i > -1) { currentTest.tween(dots[i]); } }
在正常的項目執行過程當中,咱們會使用jQuery.animate繪製大量元素嗎?有人可能會,但我不會這麼作。
GSAP相對於jQuery的進步性,就在於集中繪製了全部須要動畫更新的元素;同時也有更多的插件供選擇,能夠每過一幀改變動多的類型,而非僅僅是CSS樣式。
在項目中引入GSAP,須要引入如下三者:
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.4/plugins/CSSPlugin.min.js"></script> <script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.4/easing/EasePack.min.js"></script> <script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.4/TweenLite.min.js"></script>
耗費三個鏈接,和近25Kb來加載這個組件,得到在有很大繪製任務時更快的動畫實現,這個代價值得不值得,仍是須要在具體需求具體分析了。
在最後,推薦一篇高大上全的文章:《編寫快速、高效的JavaScript代碼》。引用文中的一句話,作結語吧:
「正如咱們所見,在JavaScript的引擎世界裏面,有許多的隱藏的性能陷阱。但事實上並無性能提升的銀彈。只有當你在測試環境中結合一系列的優化,你纔會意識到最大的性能獲益。」
更新list
v1.1
前端不少測試都不乏前人,早在HTML4時代就有人測試GUI Benchmark了:GuiMark,從他給出的用例來看,他主要測試了迴流和重繪同時存在的情形。也能夠參考一下測試方式。