摘要:本文是求索的動(dòng)畫快于嗎為何的續(xù)文。沒有集中繪制,每個(gè)都在一個(gè)事件回調(diào)函數(shù)上下文中處理,有多少個(gè)就有多少個(gè)上下文有集中繪制。測試過程中為了比較好的效果用了隨機(jī)數(shù)。
本文是求索:GSAP的動(dòng)畫快于jQuery嗎?為何? 的續(xù)文。GSAP是一個(gè)js動(dòng)畫插件,它聲稱“20x faster than jQuery”,是什么讓它這么快呢?
每當(dāng)有這樣的問題的時(shí)候,我們可以通過以下步驟來確定一個(gè)未知的解決方案的性能優(yōu)化是怎么做到/偽造的:
黑盒:從官方用例來看,究竟有多快,快在哪兒
白盒:看看官方用例之內(nèi),框架怎么做到優(yōu)化的
do { 提出假設(shè),自己構(gòu)建用例測試 } while (假設(shè)沒有得到驗(yàn)證);
得出結(jié)論
對(duì)前文的一些補(bǔ)充本文是整個(gè)探究過程的后兩個(gè)部分。
本文可能需要你知道的一些知識(shí)
requestAnimationFrame相關(guān):
MSDN
DEV.OPERA 翻譯
瀏覽器工作原理
瀏覽器的渲染原理簡介
瀏覽器的工作原理:新式網(wǎng)絡(luò)瀏覽器幕后揭秘
瀏覽器提供的調(diào)試工具
MSDN - IE11的UI響應(yīng)測量工具
developers.google - chrome的快照相關(guān)頁面
注:這里沒有更好的inspector快照內(nèi)容介紹(翻譯的,或是足夠詳盡的),如果你知道的話不妨告訴我喲~
為了下文閱讀方便,這里從profiler得到的結(jié)果角度,提一下瀏覽器里,從定時(shí)器被激活,到用戶看到圖形的主要流程:
CHROME
腳本運(yùn)行(黃色)->回流與重計(jì)算(紫色)->重繪(綠色)->其他(白色,探查器的相關(guān)內(nèi)容)
IE11
加載(深藍(lán)色)->腳本運(yùn)行(紅色)->內(nèi)存垃圾回收(黃色)->回流與重計(jì)算(綠色)->重繪(紫色)->其他(灰色,探查器的相關(guān)內(nèi)容)
注意到chrome和IE11的重繪與回流/重計(jì)算的表示顏色是相反的。
下文中的測試,chrome的計(jì)時(shí)區(qū)間是一個(gè)gc的間隔(50s左右)(因?yàn)樵赾hrome里,F(xiàn)PS和內(nèi)存占用有比較小的反相關(guān),內(nèi)存占用越多,F(xiàn)PS越低),而IE則采用10s的間隔(因?yàn)樵贗E 11里,F(xiàn)PS和內(nèi)存占用看不出相關(guān)性)。
同時(shí),不對(duì)不支持requestAnimationFrame的瀏覽器做出評(píng)測。
注:這里也沒有測試firefox,因?yàn)槟壳斑€沒有找到靠譜的測量UI響應(yīng)的功能,如果有請(qǐng)@我,多謝
最后,為了下文比較方便,我們?cè)赾hrome和IE11下再次測試官方給的用例,獲得表格(單位FPS,為平均值,后面均一樣,不再次說明):
瀏覽器 | chrome | IE 11 |
GSAP test: jQuery | 14 | 22 |
GSAP test: GSAP | 45 | 53 |
根據(jù)前文所述,我們得到以下假設(shè):
jQuery的定時(shí)器采用的是setInterval,受到瀏覽器重繪上限的控制,而GSAP采用requestAnimationFrame,完全將重繪交給瀏覽器管理,以獲得更好地重繪性能
jQuery每次都是多帶帶修改一個(gè)DOM的style,而GSAP是計(jì)算離線的style,然后再賦給DOM。這導(dǎo)致了不一樣的時(shí)間開銷。
jQuery沒有集中繪制,每個(gè)DOM都在一個(gè)事件回調(diào)函數(shù)上下文中處理,有多少個(gè)DOM就有多少個(gè)上下文;GSAP有集中繪制。同時(shí)jQuery是過程化的,GSAP是面向?qū)ο蟮摹_@讓jQuery非常難以做到集中控制繪制。jQuery會(huì)將DOM的引用一路傳遞到最終改變DOM的style函數(shù)中,這在調(diào)用過程中也會(huì)非常浪費(fèi)空間。這些也都導(dǎo)致了不一樣的時(shí)間開銷。
是這樣的嗎?
測試1:setTimeout vs requestAnimationFrame之前已經(jīng)有這樣的測試了:《setInterval與requestAnimationFrame的時(shí)間間隔測試》,但有以下弊病:
在那個(gè)測試沒有進(jìn)行實(shí)際的任務(wù),僅僅是空循環(huán)判斷時(shí)間而已
——改進(jìn):在這里我做了一個(gè)繪制隨機(jī)div的操作,一方面盡量接近GSAP的用例,另一方面盡量簡短,以減少變量對(duì)測試的影響。
測試所用的時(shí)間測量非常不準(zhǔn)確
——改進(jìn):測量結(jié)果,用的是chrome和IE11瀏覽器本身提供的原生性能分析工具,以取得盡量準(zhǔn)確的幀率。
在body內(nèi)部填入N(N=500,可以改變這個(gè)值)個(gè)div,設(shè)置它們的圓角、背景屬性。然后在每幀里面隨機(jī)改變他們的top、left(對(duì)應(yīng)著回流)、transform(對(duì)應(yīng)著重繪)值,這樣,可以讓每幀的繪制里頁面常見的繪制操作耗時(shí)均等。大致就是要繪制以下的效果。
測試代碼如下:
http://jsfiddle.net/humphry/HNysW/4/
CHROME結(jié)果:
setTimeout(fn,10)
setTimeout(fn,13)
setTimeout(fn,16)
requestAnimationFrame
setInterval(fn,16)
- “我去,幀率沒有太大區(qū)別嘛?!?br>
- “該不是因?yàn)槲覀儧]有測IE嘛?”
恩,那么我們進(jìn)入IE11,。下圖是requestAnimationFrame的測試結(jié)果,可惜沒有統(tǒng)計(jì),也不支持導(dǎo)出數(shù)據(jù),不能知道平均幀率。
我將setInterval的結(jié)果印在requestAnimationFrame上,兩者的對(duì)比可見下圖:
- “并沒有沒有顯著的提升啊?!?br>
- “看起來一定是我們沒有夠數(shù)據(jù)量的緣故。”
來吧,調(diào)節(jié)點(diǎn)的數(shù)量:
我們現(xiàn)在放進(jìn)去1000個(gè)小點(diǎn),我們?cè)贗E11下的結(jié)果對(duì)比以下:
- “要說有那么一點(diǎn)優(yōu)化,卻是一點(diǎn)優(yōu)化都看不出來的感覺呢?!?br>
- “看起來一定是我們沒有測小繪制壓力的緣故?!?/p>
或者調(diào)節(jié)成200個(gè)小點(diǎn)呢,我們?cè)贗E11下幀率對(duì)比一下:
再綜合一下數(shù)據(jù):
小點(diǎn)個(gè)數(shù) | 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 |
- “為什么,為什么看不出來效果呀 擦 擦”
- “阿瑪,我們不是被涮了吧!”
- “不可能!他們說得真真兒的,究竟是什么地方不對(duì)呢……我們是不是把回流和重繪弄得太均勻了啊”
……好吧,那么我構(gòu)造一個(gè)這樣的文檔結(jié)構(gòu),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; }
大概像是這樣。
這個(gè)時(shí)候改變最里面的div的寬度,就會(huì)導(dǎo)致大面積的回流了。
http://jsfiddle.net/humphry/SwLY7/1/
可以看到,占大比例的是回流(紫色部分)。hover到layout處,可以看到:
全員參與回流,CPU什么的一定很帶感呢。
結(jié)果:
參與嵌套的DIV數(shù)量 (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 | (崩潰) |
我們來設(shè)置陰影,以期獲得較長的重繪時(shí)間:
一閃一閃亮晶晶~
http://jsfiddle.net/humphry/XdFqR/1/
如圖,綠色的重繪占據(jù)了大部分。
結(jié)果:
閃爍的星星數(shù)量(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下是有一些提升,但是也沒有達(dá)到用例那么明顯,達(dá)到兩倍的關(guān)系呀阿瑪?!?/p>
我們來再回顧一次結(jié)果:
我們前面的三個(gè)測試:
隨機(jī)重排、縮放圓點(diǎn)測試:測試回流+重繪
嵌套inline-block大面積回流測試:測試回流
隨機(jī)陰影模糊半徑測試:測試重繪
基本上可以說明,在低渲染壓力/中等渲染壓力/高渲染壓力三種場景中,requestAnimationFrame里面的重繪性能平均比setInterval快1~5幀左右,而回流性能則沒有很大影響。
那么,GSAP是如何讓用例出現(xiàn)這么大的區(qū)別呢?chrome里快2倍,IE11里面快1倍,這不是一個(gè)輕易換用requestAnimationFrame就可以達(dá)到的結(jié)果。
我想到,setInterval這個(gè)函數(shù)其實(shí)并不是一次觸發(fā),而是針對(duì)每個(gè)DOM觸發(fā)的。在此之前的所有測試,皆是集中的計(jì)時(shí)器。因此,有了第四個(gè)測試:
測試4:集中計(jì)時(shí)器 VS 分散計(jì)時(shí)器 VS 集中rAF VS 分散rAF第四個(gè)測試,就是將前面的第一個(gè)和第三個(gè)測試改一下,將集中改成分散。方案是,擴(kuò)展HTMLDivElement原型方法,然后讓每個(gè)DOM調(diào)用它,即可模擬在官方的jQuery測試用例中發(fā)生的事情。
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/
得到結(jié)果:
測試1·改(去中心化的重繪/requestAnimationFrame)
圓點(diǎn)數(shù)量 | 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)
閃爍的陰影數(shù)量 | 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 |
這個(gè)表中終于出現(xiàn)了非常大的數(shù)據(jù)波動(dòng),也符合GSAP的的測試結(jié)果,我們可以得出結(jié)論了。
得出結(jié)論比較直觀的所有結(jié)果比較:
setInterval是否比requestAnimationFrame更慢?
不是的。上表中的數(shù)據(jù)可以表明這一點(diǎn),在短的回調(diào)里,造成重繪的相關(guān)代碼,requestAnimationFrame比setInterval稍微快一些;但是在長回調(diào)函數(shù)(多次構(gòu)造requestAnimationFrame,它們會(huì)被合到一個(gè)里面)里,requestAnimationFrame不比setInterval快。
這也是《理解WebKit:渲染主循環(huán)main loop和rAF》里說:“回調(diào)函數(shù)不能太大,不能占用太長時(shí)間,否則會(huì)影響頁面的響應(yīng)和繪制的頻率”的原因。
requestAnimationFrame最主要的意義,是降幀而非升幀,以防止丟幀。它的目的更類似于垂直同步,而非越快越好。
MSDN: 幀率不等或跳幀會(huì)使人感覺你的站點(diǎn)速度緩慢。如果降低動(dòng)畫速度可以減少跳幀并有助于保持幀率一致,它可以使人感覺站點(diǎn)速度更快。
閱讀更多:http://creativejs.com/resources/requestanimationframe/
集中定時(shí)器造成重繪是否比分散定時(shí)器造成重繪快?
是的。這也是GSAP更快的原因。
測試需要改進(jìn)嗎?
我認(rèn)為需要。測試過程中為了比較好的效果用了隨機(jī)數(shù)。其實(shí)生成隨機(jī)數(shù)的過程中也耗費(fèi)了一定的時(shí)間,更好的測試中,可以用線性的變化替換隨機(jī)離散的數(shù)值變化,數(shù)據(jù)會(huì)更加穩(wěn)定。
同時(shí),沒有測試firefox,很可惜,到目前為止,筆者依然沒有找到一款好使的分析UI的插件。
GSAP的用例是否說明了GSAP快于jQuery呢?
是的,這可以說明GSAP更快。但并非是在任何時(shí)候都更快。在我們需要粒子系統(tǒng)時(shí)快,在我們只需要繪制一兩個(gè)小交互時(shí),它沒有提供非常明顯的性能優(yōu)化的可能性。后者可以直接用CSS3 Animation/Translation做,或者用jQuery達(dá)到全瀏覽器兼容。若出現(xiàn)了粒子系統(tǒng)這樣的大量元素重繪的需求,用GSAP是很好的選擇。
造成GSAP更快的原因,是由于jQuery的處理方式,非常不適合繪制大量節(jié)點(diǎn)。
再次摘抄用例:
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]); } }
在正常的項(xiàng)目執(zhí)行過程中,我們會(huì)使用jQuery.animate繪制大量元素嗎?有人可能會(huì),但我不會(huì)這么做。
GSAP相對(duì)于jQuery的進(jìn)步性,就在于集中繪制了所有需要?jiǎng)赢嫺碌脑?;同時(shí)也有更多的插件供選擇,可以每過一幀改變更多的類型,而非僅僅是CSS樣式。
在項(xiàng)目中引入GSAP,需要引入以下三者:
耗費(fèi)三個(gè)連接,和近25Kb來加載這個(gè)組件,獲得在有很大繪制任務(wù)時(shí)更快的動(dòng)畫實(shí)現(xiàn),這個(gè)代價(jià)值得不值得,還是需要在具體需求具體分析了。
在最后,推薦一篇高大上全的文章:《編寫快速、高效的JavaScript代碼》。引用文中的一句話,做結(jié)語吧:
“正如我們所見,在JavaScript的引擎世界里面,有許多的隱藏的性能陷阱。但事實(shí)上并沒有性能提高的銀彈。只有當(dāng)你在測試環(huán)境中結(jié)合一系列的優(yōu)化,你才會(huì)意識(shí)到最大的性能獲益?!?/p>
更新list
v1.1
前端很多測試都不乏前人,早在HTML4時(shí)代就有人測試GUI Benchmark了:GuiMark,從他給出的用例來看,他主要測試了回流和重繪同時(shí)存在的情形。也可以參考一下測試方式。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/87463.html
摘要:本文已完結(jié),請(qǐng)看下文求索的動(dòng)畫快于嗎為何續(xù)本文源自對(duì)問題動(dòng)畫性能優(yōu)于的原理是什么的回答。是這樣的嗎請(qǐng)看下文求索的動(dòng)畫快于嗎為何續(xù) 本文已完結(jié),請(qǐng)看下文: > 求索:GSAP的動(dòng)畫快于jQuery嗎?為何?/續(xù) 本文源自對(duì)問題《GSAP js動(dòng)畫性能優(yōu)于jQuery的原理是什么?》的回答。GSAP是一個(gè)js動(dòng)畫插件,它聲稱20x faster than jQuery,是什么讓...
摘要:現(xiàn)在又多了一種實(shí)現(xiàn)動(dòng)畫的方案,那就是還在草案當(dāng)中的方法。這個(gè)方法就是傳遞給的回調(diào)函數(shù)。為回調(diào)函數(shù)一個(gè)簡單的例子模擬一個(gè)進(jìn)度條動(dòng)畫,初始寬度為在函數(shù)中將進(jìn)度加然后再更新到寬度上,在進(jìn)度達(dá)到之前,一直重復(fù)這一過程。 HTML5/CSS3時(shí)代,我們要在web里做動(dòng)畫選擇其實(shí)已經(jīng)很多了: 你可以用CSS3的animattion+keyframes; 你也可以用css3的transition...
摘要:現(xiàn)在又多了一種實(shí)現(xiàn)動(dòng)畫的方案,那就是還在草案當(dāng)中的方法。這個(gè)方法就是傳遞給的回調(diào)函數(shù)。為回調(diào)函數(shù)一個(gè)簡單的例子模擬一個(gè)進(jìn)度條動(dòng)畫,初始寬度為在函數(shù)中將進(jìn)度加然后再更新到寬度上,在進(jìn)度達(dá)到之前,一直重復(fù)這一過程。 HTML5/CSS3時(shí)代,我們要在web里做動(dòng)畫選擇其實(shí)已經(jīng)很多了: 你可以用CSS3的animattion+keyframes; 你也可以用css3的transition...
摘要:現(xiàn)在又多了一種實(shí)現(xiàn)動(dòng)畫的方案,那就是還在草案當(dāng)中的方法。這個(gè)方法就是傳遞給的回調(diào)函數(shù)。為回調(diào)函數(shù)一個(gè)簡單的例子模擬一個(gè)進(jìn)度條動(dòng)畫,初始寬度為在函數(shù)中將進(jìn)度加然后再更新到寬度上,在進(jìn)度達(dá)到之前,一直重復(fù)這一過程。 HTML5/CSS3時(shí)代,我們要在web里做動(dòng)畫選擇其實(shí)已經(jīng)很多了: 你可以用CSS3的animattion+keyframes; 你也可以用css3的transition...
摘要:雖然沒有視覺效果,但這就是基本的值動(dòng)畫。有專門的位置可以查詢緩動(dòng)函數(shù)。另外,不要期望在不支持的瀏覽器上做動(dòng)畫。是專業(yè)動(dòng)畫庫,在大部分情況下,它也具備更好的動(dòng)畫性能。 說到在網(wǎng)頁里創(chuàng)建動(dòng)畫,你可能很快會(huì)想到j(luò)Query的animate()方法,或者css3的animation和transition?,F(xiàn)在,本文將介紹另一個(gè)web動(dòng)畫的可選方案,GSAP。 GSAP的全名是GreenSock...
閱讀 3670·2023-04-25 20:09
閱讀 3831·2022-06-28 19:00
閱讀 3193·2022-06-28 19:00
閱讀 3227·2022-06-28 19:00
閱讀 3341·2022-06-28 19:00
閱讀 2999·2022-06-28 19:00
閱讀 3236·2022-06-28 19:00
閱讀 2777·2022-06-28 19:00