瀏覽器器內(nèi)核拿到內(nèi)容(渲染線程接收請求物臂,加載網(wǎng)頁并渲染網(wǎng)頁),渲染大概可以劃分成以下幾個步驟:
- 解析HTML(HTML Parser)
- 構(gòu)建DOM樹(DOM Tree)
- 解析css構(gòu)建render樹(將CSS代碼解析成樹形的數(shù)據(jù)結(jié)構(gòu)瓤介,然后結(jié)合DOM合并成render樹)
- 布局render樹(Layout/reflow)么抗,負(fù)責(zé)各元素尺寸、位置的計算
- 繪制render樹(paint)砸抛,繪制頁面像素信息
- 瀏覽器會將各層的信息發(fā)送給GPU(GPU進程:最多一個评雌,用于3D繪制等),GPU會將各層合成(composite)直焙,顯示在屏幕上
這里先解釋一下幾個概念:
-DOM Tree:瀏覽器將HTML解析成樹形的數(shù)據(jù)結(jié)構(gòu)景东。
-CSS Rule Tree:瀏覽器將CSS解析成樹形的數(shù)據(jù)結(jié)構(gòu)。
-Render Tree: DOM和CSSOM合并后生成Render Tree奔誓。
-layout: 有了Render Tree斤吐,瀏覽器已經(jīng)能知道網(wǎng)頁中有哪些節(jié)點、各個節(jié)點的CSS定義以及他們的從屬關(guān)系,從而去計算出每個節(jié)點在屏幕中的位置曲初。
-painting: 按照算出來的規(guī)則体谒,通過顯卡,把內(nèi)容畫到屏幕上臼婆。
-reflow(回流):當(dāng)瀏覽器發(fā)現(xiàn)某個部分發(fā)生了點變化影響了布局抒痒,需要倒回去重新渲染,這個回退的過程叫 reflow颁褂。reflow 會從 這個 root frame 開始遞歸往下故响,依次計算所有的結(jié)點幾何尺寸和位置。
-repaint(重繪):改變某個元素的背景色颁独、文字顏色彩届、邊框顏色等等不影響它周圍或內(nèi)部布局的屬性時,屏幕的一部分要重畫誓酒,但是元素的幾何尺寸沒有變樟蠕。
注意:
-display:none 的節(jié)點不會被加入Render Tree,而visibility: hidden
則會靠柑,所以寨辩,如果某個節(jié)點最開始是不顯示的,設(shè)為display:none是更優(yōu)的歼冰。
-display:none 會觸發(fā) reflow靡狞,而 visibility:hidden 只會觸發(fā) repaint,因為沒有發(fā)現(xiàn)位置變化隔嫡。
-有些情況下甸怕,比如修改了元素的樣式,瀏覽器并不會立刻reflow 或 repaint 一次腮恩,而是會把這樣的操作積攢一批梢杭,然后做一次reflow,這又叫異步 reflow 或增量異步 reflow庆揪。但是在有些情況下式曲,比如resize窗口,改變了頁面默認(rèn)的字體等缸榛。對于這些操作吝羞,瀏覽器會馬上進行 reflow。
層(layer)
瀏覽器在渲染一個頁面時内颗,會將頁面分為很多個圖層钧排,圖層有大有小,每個圖層上有一個或多個節(jié)點均澳。在渲染DOM的時候恨溜,瀏覽器所做的工作實際上是:
-獲取DOM后分割為多個圖層
-對每個圖層的節(jié)點計算樣式結(jié)果(Recalculate style–樣式重計算)
-為每個節(jié)點生成圖形和位置(Layout–回流和重布局)
-將每個節(jié)點繪制填充到圖層位圖中(Paint Setup和Paint–重繪)
-圖層作為紋理上傳至GPU
-組合多個圖層到頁面上生成最終屏幕圖像(Composite Layers–圖層重組)
紋理(texture)
這里的紋理指的是 GPU 的一個術(shù)語:可以把它想象成一個從主存儲器(例如 RAM)移動到圖像存儲器(例如 GPU 中的 VRAM)的位圖圖像(bitmap image)符衔。一旦它被移動到 GPU 中,你可以將它匹配成一個網(wǎng)格幾何體(mesh geometry)糟袁,在 Chrome 中使用紋理來從 GPU 上獲得大塊的頁面內(nèi)容判族。通過將紋理應(yīng)用到一個非常簡單的矩形網(wǎng)格就能很容易匹配不同的位置(position)和變形(transformation),這也就是 3D CSS 的工作原理项戴。
加速原理
瀏覽器接收到頁面文檔后形帮,會將文檔中的標(biāo)記語言解析為DOM樹。DOM樹和CSS結(jié)合后形成瀏覽器構(gòu)建頁面的渲染樹周叮。渲染樹中包含了大量的渲染元素辩撑,每一個渲染元素會被分到一個圖層中,每個圖層又會被加載到GPU形成渲染紋理仿耽,而圖層在GPU中 transform 是不會觸發(fā) repaint 的合冀,這一點非常類似3D繪圖功能,最終這些使用 transform 的圖層都會由獨立的合成器進程進行處理项贺。
合成層創(chuàng)建標(biāo)準(zhǔn)
Chrome中滿足以下任意情況就會創(chuàng)建圖層:
-3D或透視變換(perspective transform)CSS屬性
-使用加速視頻解碼的節(jié)點
-擁有3D(WebGL)上下文或加速的2D上下文的節(jié)點
-混合插件(如Flash)
-對自己的opacity做CSS動畫或使用一個動畫webkit變換的元素
-擁有加速CSS過濾器的元素
-元素有一個包含復(fù)合層的后代節(jié)點(一個元素?fù)碛幸粋€子元素君躺,該子元素在自己的層里)
-元素有一個z-index較低且包含一個復(fù)合層的兄弟元素(換句話說就是該元素在復(fù)合層上面渲染)
合成層的優(yōu)點
一個元素開啟硬件加速后會變成合成層,可以獨立于普通文檔流中开缎,改動后可以避免整個頁面重繪晰洒,提升性能。
-合成層的位圖啥箭,會交由 GPU 合成,比 CPU 處理要快
-當(dāng)需要 repaint 時治宣,只需要 repaint 本身急侥,不會影響到其他的層
-對于 transform 和 opacity 效果,不會觸發(fā) layout 和 paint
注意:
-提升到合成層后合成層的位圖會交GPU處理侮邀,但請注意坏怪,僅僅只是合成的處理(把繪圖上下文的位圖輸出進行組合)需要用到GPU,生成合成層的位圖處理(繪圖上下文的工作)是需要CPU绊茧。
-當(dāng)需要repaint的時候可以只repaint本身铝宵,不影響其他層,但是paint之前還有style华畏, layout,那就意味著即使合成層只是repaint了自己鹏秋,但style和layout本身就很占用時間。
-僅僅是transform和opacity不會引發(fā)layout 和paint亡笑,那么其他的屬性不確定侣夷。
性能優(yōu)化
-提升動畫效果的元素 合成層的好處是不會影響到其他元素的繪制,因此仑乌,為了減少動畫元素對其他元素的影響百拓,從而減少paint琴锭,我們需要把動畫效果中的元素提升為合成層。 提升合成層的最好方式是使用 CSS 的 will-change屬性衙传。will-change 設(shè)置為opacity决帖、transform、top蓖捶、left地回、bottom、right 可以將元素提升為合成層腺阳。
-使用 transform 或者 opacity 來實現(xiàn)動畫效果, 這樣只需要做合成層的合并就好了落君。
-減少繪制區(qū)域 對于不需要重新繪制的區(qū)域應(yīng)盡量避免繪制,以減少繪制區(qū)域亭引。對于固定不變的區(qū)域绎速,我們期望其并不會被重繪,因此可以將其提升為獨立的合成層焙蚓,減少繪制區(qū)域纹冤,需要仔細(xì)分析頁面,區(qū)分繪制區(qū)域购公,減少重繪區(qū)域甚至避免重繪萌京。
層的重繪
對于靜態(tài) Web 頁面而言,層在第一次被繪制出來之后將不會被改變宏浩,但對于 Web 動畫知残,頁面的 DOM 元素是在不斷變換的,如果層的內(nèi)容在變換過程中發(fā)生了改變比庄,那么層將會被重繪(repaint)求妹。
需要注意的是,如果圖層中某個元素需要重繪佳窑,那么整個圖層都需要重繪制恍。比如一個圖層包含很多節(jié)點,其中一個節(jié)點發(fā)生變化神凑,都會重繪整個圖層的其他節(jié)點净神,然后生成最終的圖層位圖。所以這需要通過特殊的方式來強制這個節(jié)點擁用一個自己的圖層(translateZ(0)或者translate3d(0,0,0))溉委,CSS3的動畫也是一樣(好在絕大部分情況瀏覽器自己會為CSS3動畫的節(jié)點創(chuàng)建圖層)鹃唯。
層和CSS動畫
簡化一下上述過程,每一幀動畫瀏覽器可能需要做如下工作:
-瀏覽器解析 HTML 獲取 DOM 后分割為多個圖層(GraphicsLayer)
-對每個圖層的節(jié)點計算樣式結(jié)果(Recalculate style–樣式重計算)
-為每個節(jié)點生成圖形和位置(Layout–回流和重布局)
-將每個節(jié)點繪制填充到圖層位圖中(Paint Setup和Paint–重繪)
-圖層作為紋理(texture)上傳至 GPU
-組合多個圖層到頁面上生成最終屏幕圖像(Composite Layers–圖層重組)
如果我們需要使得動畫的性能提高薛躬,需要做的就是減少瀏覽器在動畫運行時所需要做的工作俯渤。最好的情況是,改變的屬性僅僅印象圖層的組合型宝,變換(transform)和透明度(opacity)就屬于這種情況八匠。
現(xiàn)代瀏覽器如Chrome絮爷,F(xiàn)irefox,Safari和Opera都對變換和透明度采用硬件加速梨树。
CSS 中的以下幾個屬性能觸發(fā)硬件加速:
-transform
-opacity
-filter
-will-change
3D 和 2D transform 的區(qū)別就在于坑夯,瀏覽器在頁面渲染前為3D動畫創(chuàng)建獨立的復(fù)合圖層,而在運行期間為2D動畫創(chuàng)建抡四。動畫開始時柜蜈,生成新的復(fù)合圖層并加載為GPU的紋理用于初始化 repaint。然后由GPU的復(fù)合器操縱整個動畫的執(zhí)行指巡。最后當(dāng)動畫結(jié)束時淑履,再次執(zhí)行 repaint 操作刪除復(fù)合圖層。
強制使用GPU渲染
為了避免 2D transform 動畫在開始和結(jié)束時發(fā)生的 repaint 操作藻雪,我們可以硬編碼一些樣式來解決這個問題:
.example1 {
transform: translateZ(0);
}
.example2 {
transform: rotateZ(360deg);
}
這段代碼的作用就是讓瀏覽器執(zhí)行 3D transform秘噪。瀏覽器通過該樣式創(chuàng)建了一個獨立圖層,圖層中的動畫則有GPU進行預(yù)處理并且觸發(fā)了硬件加速勉耀。
如果某一個元素的背后是一個復(fù)雜元素指煎,那么該元素的 repaint 操作就會耗費大量的資源,此時也可以使用上面的技巧來減少性能開銷便斥。
使用硬件加速的注意事項
-內(nèi)存至壤。如果GPU加載了大量的紋理,那么很容易就會發(fā)生內(nèi)容問題枢纠,這一點在移動端瀏覽器上尤為明顯像街,所以,一定要牢記不要讓頁面的每個元素都使用硬件加速晋渺。
-使用GPU渲染會影響字體的抗鋸齒效果宅广。這是因為GPU和CPU具有不同的渲染機制。即使最終硬件加速停止了些举,文本還是會在動畫期間顯示得很模糊。