正巧看到在送書(shū),于是乎找了找自己博客上記錄過(guò)的一些東西來(lái)及其無(wú)恥的蹭書(shū)了~~~
小廣告:更多內(nèi)容可以看我的博客
最近拜讀了一下html5rocks上幾位大神寫(xiě)的一篇關(guān)于CSS3動(dòng)畫(huà)性能優(yōu)化的文章,學(xué)到了很多,在這里記錄一下,其中的知識(shí)都是來(lái)源于這倆篇文章,我只是截取了其中比較關(guān)注的內(nèi)容出來(lái),原文地址High Performance Animations及Accelerated Rendering in Chrome
現(xiàn)代瀏覽器在使用CSS3動(dòng)畫(huà)時(shí),以下四種情形繪制的效率較高,分別是:
* 改變位置
* 改變大小
* 旋轉(zhuǎn)
* 改變透明度
首先要了解CSS的圖層的概念(Chrome瀏覽器)
瀏覽器在渲染一個(gè)頁(yè)面時(shí),會(huì)將頁(yè)面分為很多個(gè)圖層,圖層有大有小,每個(gè)圖層上有一個(gè)或多個(gè)節(jié)點(diǎn)。在渲染DOM的時(shí)候,瀏覽器所做的工作實(shí)際上是:
1. 獲取DOM后分割為多個(gè)圖層
2. 對(duì)每個(gè)圖層的節(jié)點(diǎn)計(jì)算樣式結(jié)果(Recalculate style--樣式重計(jì)算)
3. 為每個(gè)節(jié)點(diǎn)生成圖形和位置(Layout--回流和重布局)
4. 將每個(gè)節(jié)點(diǎn)繪制填充到圖層位圖中(Paint Setup和Paint--重繪)
5. 圖層作為紋理上傳至GPU
6. 符合多個(gè)圖層到頁(yè)面上生成最終屏幕圖像(Composite Layers--圖層重組)
Chrome中滿足以下任意情況就會(huì)創(chuàng)建圖層:
* 3D或透視變換(perspective transform)CSS屬性
* 使用加速視頻解碼的<video>
節(jié)點(diǎn)
* 擁有3D(WebGL)上下文或加速的2D上下文的<canvas>
節(jié)點(diǎn)
* 混合插件(如Flash)
* 對(duì)自己的opacity做CSS動(dòng)畫(huà)或使用一個(gè)動(dòng)畫(huà)webkit變換的元素
* 擁有加速CSS過(guò)濾器的元素
* 元素有一個(gè)包含復(fù)合層的后代節(jié)點(diǎn)(一個(gè)元素?fù)碛幸粋€(gè)子元素,該子元素在自己的層里)
* 元素有一個(gè)z-index
較低且包含一個(gè)復(fù)合層的兄弟元素(換句話說(shuō)就是該元素在復(fù)合層上面渲染)
需要注意的是,如果圖層中某個(gè)元素需要重繪,那么整個(gè)圖層都需要重繪。比如一個(gè)圖層包含很多節(jié)點(diǎn),其中有個(gè)gif圖,gif圖的每一幀,都會(huì)重回整個(gè)圖層的其他節(jié)點(diǎn),然后生成最終的圖層位圖。所以這需要通過(guò)特殊的方式來(lái)強(qiáng)制gif圖屬于自己一個(gè)圖層(translateZ(0)
或者translate3d(0,0,0)
),CSS3的動(dòng)畫(huà)也是一樣(好在絕大部分情況瀏覽器自己會(huì)為CSS3動(dòng)畫(huà)的節(jié)點(diǎn)創(chuàng)建圖層)
簡(jiǎn)化一下上述過(guò)程,每一幀動(dòng)畫(huà)瀏覽器可能需要做如下工作:
1. 計(jì)算需要被加載到節(jié)點(diǎn)上的樣式結(jié)果(Recalculate style--樣式重計(jì)算)
2. 為每個(gè)節(jié)點(diǎn)生成圖形和位置(Layout--回流和重布局)
3. 將每個(gè)節(jié)點(diǎn)填充到圖層中(Paint Setup和Paint--重繪)
4. 組合圖層到頁(yè)面上(Composite Layers--圖層重組)
如果我們需要使得動(dòng)畫(huà)的性能提高,需要做的就是減少瀏覽器在動(dòng)畫(huà)運(yùn)行時(shí)所需要做的工作。最好的情況是,改變的屬性僅僅印象圖層的組合,變換(transform
)和透明度(opacity
)就屬于這種情況
現(xiàn)代瀏覽器如Chrome,F(xiàn)irefox,Safari和Opera都對(duì)變換和透明度采用硬件加速,但I(xiàn)E10+不是很確定是否硬件加速
有些節(jié)點(diǎn),當(dāng)你改變他時(shí),會(huì)需要重新布局(這也意味著需要重新計(jì)算其他被影響的節(jié)點(diǎn)的位置和大?。_@種情況下,被影響的DOM樹(shù)越大(可見(jiàn)節(jié)點(diǎn)),重繪所需要的時(shí)間就會(huì)越長(zhǎng),而渲染一幀動(dòng)畫(huà)的時(shí)間也相應(yīng)變長(zhǎng)。所以需要盡力避免這些屬性
一些常用的改變時(shí)會(huì)觸發(fā)重布局的屬性:
盒子模型相關(guān)屬性會(huì)觸發(fā)重布局:
* width
* height
* padding
* margin
* display
* border-width
* border
* min-height
定位屬性及浮動(dòng)也會(huì)觸發(fā)重布局:
* top
* bottom
* left
* right
* position
* float
* clear
改變節(jié)點(diǎn)內(nèi)部文字結(jié)構(gòu)也會(huì)觸發(fā)重布局:
* text-align
* overflow-y
* font-weight
* overflow
* font-family
* line-height
* vertival-align
* white-space
* font-size
這么多常用屬性都會(huì)觸發(fā)重布局,可以看到,他們的特點(diǎn)就是可能修改整個(gè)節(jié)點(diǎn)的大小或位置,所以會(huì)觸發(fā)重布局
如果在網(wǎng)頁(yè)中使用CSS的類來(lái)對(duì)節(jié)點(diǎn)做狀態(tài)標(biāo)記,當(dāng)這些節(jié)點(diǎn)的狀態(tài)標(biāo)記類修改時(shí),將會(huì)觸發(fā)節(jié)點(diǎn)的重繪和重布局。所以在節(jié)點(diǎn)上使用CSS類來(lái)做狀態(tài)比較是代價(jià)很昂貴的
修改時(shí)只觸發(fā)重繪的屬性有:
* color
* border-style
* border-radius
* visibility
* text-decoration
* background
* background-image
* background-position
* background-repeat
* background-size
* outline-color
* outline
* outline-style
* outline-width
* box-shadow
這樣可以看到,這些屬性都不會(huì)修改節(jié)點(diǎn)的大小和位置,自然不會(huì)觸發(fā)重布局,但是節(jié)點(diǎn)內(nèi)部的渲染效果進(jìn)行了改變,所以只需要重繪就可以了
在重繪時(shí),這些節(jié)點(diǎn)會(huì)被加載到GPU中進(jìn)行重繪,這對(duì)移動(dòng)設(shè)備如手機(jī)的影響還是很大的。因?yàn)镃PU不如臺(tái)式機(jī)或筆記本電腦,所以繪畫(huà)巫妖的時(shí)間更長(zhǎng)。而且CPU與GPU之間的有較大的帶寬限制,所以紋理的上傳需要一定時(shí)間
需要注意的是,上面那些觸發(fā)重繪的屬性里面沒(méi)有opacity
(透明度),很奇怪不是嗎?實(shí)際上透明度的改變后,GPU在繪畫(huà)時(shí)只是簡(jiǎn)單的降低之前已經(jīng)畫(huà)好的紋理的alpha值來(lái)達(dá)到效果,并不需要整體的重繪。不過(guò)這個(gè)前提是這個(gè)被修改opacity
本身必須是一個(gè)圖層,如果圖層下還有其他節(jié)點(diǎn),GPU也會(huì)將他們透明化
在Blink和WebKit的瀏覽器中,一當(dāng)一個(gè)節(jié)點(diǎn)被設(shè)定了透明度的相關(guān)過(guò)渡效果或動(dòng)畫(huà)時(shí),瀏覽器會(huì)將其作為一個(gè)單獨(dú)的圖層,但很多開(kāi)發(fā)者使用translateZ(0)
或者translate3d(0,0,0)
去使瀏覽器創(chuàng)建圖層。這種方式可以消除在動(dòng)畫(huà)開(kāi)始之前的圖層創(chuàng)建時(shí)間,使得動(dòng)畫(huà)盡快開(kāi)始(創(chuàng)建圖層和繪制圖層還是比較慢的),而且不會(huì)隨著抗鋸齒而導(dǎo)出突變。不過(guò)這種方法需要節(jié)制,否則會(huì)因?yàn)閯?chuàng)建過(guò)多的圖層導(dǎo)致崩潰
Chrome中,非根圖層以及透明圖層使用grayscale antialiasing而不是subpixel antialiasing,如果抗鋸齒方法變化,這個(gè)效果將會(huì)非常顯著。如果你打算預(yù)處理一個(gè)節(jié)點(diǎn)而不打算等到動(dòng)畫(huà)開(kāi)始,可以通過(guò)這種強(qiáng)迫瀏覽器創(chuàng)建圖層的方式進(jìn)行
我們通過(guò)節(jié)點(diǎn)的transform
可以修改節(jié)點(diǎn)的位置、旋轉(zhuǎn)、大小等。我們平常會(huì)使用left
和top
屬性來(lái)修改節(jié)點(diǎn)的位置,但正如上面所述,left
和top
會(huì)觸發(fā)重布局,修改時(shí)的代價(jià)相當(dāng)大。取而代之的更好方法是使用translate
,這個(gè)不會(huì)觸發(fā)重布局
我們經(jīng)常面臨一個(gè)抉擇:是使用JavaScript的動(dòng)畫(huà)還是使用CSS的動(dòng)畫(huà),下面將對(duì)比一下這兩種方式
缺點(diǎn):JavaScript在瀏覽器的主線程中運(yùn)行,而其中還有很多其他需要運(yùn)行的JavaScript、樣式計(jì)算、布局、繪制等對(duì)其干擾。這也就導(dǎo)致了線程可能出現(xiàn)阻塞,從而造成丟幀的情況。
優(yōu)點(diǎn):JavaScript的動(dòng)畫(huà)與CSS預(yù)先定義好的動(dòng)畫(huà)不同,可以在其動(dòng)畫(huà)過(guò)程中對(duì)其進(jìn)行控制:開(kāi)始、暫停、回放、中止、取消都是可以做到的。而且一些動(dòng)畫(huà)效果,比如視差滾動(dòng)效果,只有JavaScript能夠完成
缺點(diǎn):缺乏強(qiáng)大的控制能力。而且很難以有意義的方式結(jié)合到一起,使得動(dòng)畫(huà)變得復(fù)雜且易于出問(wèn)題。
優(yōu)點(diǎn):瀏覽器可以對(duì)動(dòng)畫(huà)進(jìn)行優(yōu)化。它必要時(shí)可以創(chuàng)建圖層,然后在主線程之外運(yùn)行。
Google目前正在探究通過(guò)JS的多線程(Web Workers)來(lái)提供更好的動(dòng)畫(huà)效果,而不會(huì)觸發(fā)重布局及樣式重計(jì)算
動(dòng)畫(huà)給予了頁(yè)面豐富的視覺(jué)體驗(yàn)。我們應(yīng)該盡力避免使用會(huì)觸發(fā)重布局和重繪的屬性,以免失幀。最好提前申明動(dòng)畫(huà),這樣能讓瀏覽器提前對(duì)動(dòng)畫(huà)進(jìn)行優(yōu)化。由于GPU的參與,現(xiàn)在用來(lái)做動(dòng)畫(huà)的最好屬性是如下幾個(gè):
* opacity
* translate
* rotate
* scale
也許會(huì)有一些新的方式使得可以使用JavaScript做出更好的沒(méi)有限制的動(dòng)畫(huà),而且不用擔(dān)心主線程的阻塞問(wèn)題。但在那之前,還是好好考慮下如何做出流暢的動(dòng)畫(huà)吧
聯(lián)系客服