摘要:本文是求索的動畫快于嗎為何的續文。沒有集中繪制,每個都在一個事件回調函數上下文中處理,有多少個就有多少個上下文有集中繪制。測試過程中為了比較好的效果用了隨機數。
本文是求索:GSAP的動畫快于jQuery嗎?為何? 的續文。GSAP是一個js動畫插件,它聲稱“20x faster than jQuery”,是什么讓它這么快呢?
每當有這樣的問題的時候,我們可以通過以下步驟來確定一個未知的解決方案的性能優化是怎么做到/偽造的:
黑盒:從官方用例來看,究竟有多快,快在哪兒
白盒:看看官方用例之內,框架怎么做到優化的
do { 提出假設,自己構建用例測試 } while (假設沒有得到驗證);
得出結論
對前文的一些補充本文是整個探究過程的后兩個部分。
本文可能需要你知道的一些知識
requestAnimationFrame相關:
MSDN
DEV.OPERA 翻譯
瀏覽器工作原理
瀏覽器的渲染原理簡介
瀏覽器的工作原理:新式網絡瀏覽器幕后揭秘
瀏覽器提供的調試工具
MSDN - IE11的UI響應測量工具
developers.google - chrome的快照相關頁面
注:這里沒有更好的inspector快照內容介紹(翻譯的,或是足夠詳盡的),如果你知道的話不妨告訴我喲~
為了下文閱讀方便,這里從profiler得到的結果角度,提一下瀏覽器里,從定時器被激活,到用戶看到圖形的主要流程:
CHROME
腳本運行(黃色)->回流與重計算(紫色)->重繪(綠色)->其他(白色,探查器的相關內容)
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 |
根據前文所述,我們得到以下假設:
jQuery的定時器采用的是setInterval,受到瀏覽器重繪上限的控制,而GSAP采用requestAnimationFrame,完全將重繪交給瀏覽器管理,以獲得更好地重繪性能
jQuery每次都是多帶帶修改一個DOM的style,而GSAP是計算離線的style,然后再賦給DOM。這導致了不一樣的時間開銷。
jQuery沒有集中繪制,每個DOM都在一個事件回調函數上下文中處理,有多少個DOM就有多少個上下文;GSAP有集中繪制。同時jQuery是過程化的,GSAP是面向對象的。這讓jQuery非常難以做到集中控制繪制。jQuery會將DOM的引用一路傳遞到最終改變DOM的style函數中,這在調用過程中也會非常浪費空間。這些也都導致了不一樣的時間開銷。
是這樣的嗎?
測試1:setTimeout vs requestAnimationFrame之前已經有這樣的測試了:《setInterval與requestAnimationFrame的時間間隔測試》,但有以下弊病:
在那個測試沒有進行實際的任務,僅僅是空循環判斷時間而已
——改進:在這里我做了一個繪制隨機div的操作,一方面盡量接近GSAP的用例,另一方面盡量簡短,以減少變量對測試的影響。
測試所用的時間測量非常不準確
——改進:測量結果,用的是chrome和IE11瀏覽器本身提供的原生性能分析工具,以取得盡量準確的幀率。
在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下是有一些提升,但是也沒有達到用例那么明顯,達到兩倍的關系呀阿瑪。”
我們來再回顧一次結果:
我們前面的三個測試:
隨機重排、縮放圓點測試:測試回流+重繪
嵌套inline-block大面積回流測試:測試回流
隨機陰影模糊半徑測試:測試重繪
基本上可以說明,在低渲染壓力/中等渲染壓力/高渲染壓力三種場景中,requestAnimationFrame里面的重繪性能平均比setInterval快1~5幀左右,而回流性能則沒有很大影響。
那么,GSAP是如何讓用例出現這么大的區別呢?chrome里快2倍,IE11里面快1倍,這不是一個輕易換用requestAnimationFrame就可以達到的結果。
我想到,setInterval這個函數其實并不是一次觸發,而是針對每個DOM觸發的。在此之前的所有測試,皆是集中的計時器。因此,有了第四個測試:
測試4:集中計時器 VS 分散計時器 VS 集中rAF VS 分散rAF第四個測試,就是將前面的第一個和第三個測試改一下,將集中改成分散。方案是,擴展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,需要引入以下三者:
耗費三個連接,和近25Kb來加載這個組件,獲得在有很大繪制任務時更快的動畫實現,這個代價值得不值得,還是需要在具體需求具體分析了。
在最后,推薦一篇高大上全的文章:《編寫快速、高效的JavaScript代碼》。引用文中的一句話,做結語吧:
“正如我們所見,在JavaScript的引擎世界里面,有許多的隱藏的性能陷阱。但事實上并沒有性能提高的銀彈。只有當你在測試環境中結合一系列的優化,你才會意識到最大的性能獲益。”
更新list
v1.1
前端很多測試都不乏前人,早在HTML4時代就有人測試GUI Benchmark了:GuiMark,從他給出的用例來看,他主要測試了回流和重繪同時存在的情形。也可以參考一下測試方式。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87463.html
摘要:本文已完結,請看下文求索的動畫快于嗎為何續本文源自對問題動畫性能優于的原理是什么的回答。是這樣的嗎請看下文求索的動畫快于嗎為何續 本文已完結,請看下文: > 求索:GSAP的動畫快于jQuery嗎?為何?/續 本文源自對問題《GSAP js動畫性能優于jQuery的原理是什么?》的回答。GSAP是一個js動畫插件,它聲稱20x faster than jQuery,是什么讓...
摘要:現在又多了一種實現動畫的方案,那就是還在草案當中的方法。這個方法就是傳遞給的回調函數。為回調函數一個簡單的例子模擬一個進度條動畫,初始寬度為在函數中將進度加然后再更新到寬度上,在進度達到之前,一直重復這一過程。 HTML5/CSS3時代,我們要在web里做動畫選擇其實已經很多了: 你可以用CSS3的animattion+keyframes; 你也可以用css3的transition...
摘要:現在又多了一種實現動畫的方案,那就是還在草案當中的方法。這個方法就是傳遞給的回調函數。為回調函數一個簡單的例子模擬一個進度條動畫,初始寬度為在函數中將進度加然后再更新到寬度上,在進度達到之前,一直重復這一過程。 HTML5/CSS3時代,我們要在web里做動畫選擇其實已經很多了: 你可以用CSS3的animattion+keyframes; 你也可以用css3的transition...
摘要:現在又多了一種實現動畫的方案,那就是還在草案當中的方法。這個方法就是傳遞給的回調函數。為回調函數一個簡單的例子模擬一個進度條動畫,初始寬度為在函數中將進度加然后再更新到寬度上,在進度達到之前,一直重復這一過程。 HTML5/CSS3時代,我們要在web里做動畫選擇其實已經很多了: 你可以用CSS3的animattion+keyframes; 你也可以用css3的transition...
摘要:雖然沒有視覺效果,但這就是基本的值動畫。有專門的位置可以查詢緩動函數。另外,不要期望在不支持的瀏覽器上做動畫。是專業動畫庫,在大部分情況下,它也具備更好的動畫性能。 說到在網頁里創建動畫,你可能很快會想到jQuery的animate()方法,或者css3的animation和transition。現在,本文將介紹另一個web動畫的可選方案,GSAP。 GSAP的全名是GreenSock...
閱讀 3528·2023-04-25 20:09
閱讀 3733·2022-06-28 19:00
閱讀 3053·2022-06-28 19:00
閱讀 3071·2022-06-28 19:00
閱讀 3160·2022-06-28 19:00
閱讀 2870·2022-06-28 19:00
閱讀 3031·2022-06-28 19:00
閱讀 2628·2022-06-28 19:00