CSS動畫屬性/重繪重排組合層/GPU加速 渲染優化相關及聯繫

1、現代瀏覽器,比如chrome,firefox都支持硬件加速,GPU加速功能,開啓後,使用相關CSS屬性,3D API,canvas等,都會默認用GPU渲染的方式去繪製圖像。

瀏覽器渲染的過程,網上圖片雜且準確性出處都有待考究,關於這個過程和原理,國外有一個最權威的文檔資料:鏈接
(差不多網上的文章都是從這裏盜的內容了),濃縮一下流程,可以概括爲:
在這裏插入圖片描述
1)可以看到DOM Tree 是由HTML等標籤和Script的構建的,然後是解析Scipt標籤,這裏並行,異步,還是阻塞,可以通過script的async defer來控制,沒有加這些默認是會阻塞DOM Tree的構建。

2)解析完成後,瀏覽器引擎會通過DOM Tree 和 CSS Rule Tree 來構造 Rendering Tree。
CSS 的 Rule Tree主要是爲了完成匹配並把CSS Rule附加上Rendering Tree上的每個Element。也就是DOM結點。也就是所謂的Frame。(這裏的Rendering Tree和Frame的叫法查資料是分別在WebKit和Gecko內核的兩種叫法)
然後,計算每個Frame(也就是每個Element)的位置,這又叫layout或者reflow過程。(layout是Webkit的叫法,reflow是Gecko的叫法)

3)最後通過調用操作系統Native GUI的API繪製。

2、上面是瀏覽器的過程,這裏再重點說繪製像素前的關鍵的兩個概念,迴流Reflow(也叫重排),重繪Repaint。

Repaint——屏幕的一部分要重畫,比如某個CSS的背景色變了。但是元素的幾何尺寸沒有變。

Reflow——意味着元件的幾何尺寸變了,我們需要重新驗證並計算Render Tree。是Render Tree的一部分或全部發生了變化。這就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式佈局,所以,如果某元件的幾何尺寸發生了變化,需要重新佈局,也就叫reflow)reflow 會從這個root frame開始遞歸往下,依次計算所有的結點幾何尺寸和位置,在reflow過程中,可能會增加一些frame,比如一個文本字符串必需被包裝起來。

Reflow的成本比Repaint的成本高得多的多。DOM Tree裏的每個結點都會有reflow方法,一個結點的reflow很有可能導致子結點,甚至父點以及同級結點的reflow。在一些高性能的電腦上也許還沒什麼,但是如果reflow發生在手機上,那麼這個過程是非常痛苦和耗電的。(之前強哥就說用了容易引起reflow的方式做動畫最後手機表現上很卡)

reflow肯定會引起repaint(注:display:none會觸發reflow,而visibility:hidden只會觸發repaint,因爲沒有發現位置變化。)

3、一個可能會引起你驚訝的消息是,當你獲取請求DOM的一些值的時候也會立即引起reflow:

	offsetTop, offsetLeft, offsetWidth, offsetHeight
	scrollTop/Left/Width/Height
	clientTop/Left/Width/Height
	IE中的 getComputedStyle(), 或 currentStyle

到底那些會引起瀏覽器的這些過程,除了靠經驗和想象,也可查:
https://gist.github.com/paulirish/5d52fb081b3570c81e3a

哪些CSS屬性會引起reflow還是repaint,會不會走GPU加速,在不同瀏覽器會不會有差異,這些可查:https://csstriggers.com (神站!)

4、瀏覽器在每一幀中,都要經過下列動作

在這裏插入圖片描述

所有的渲染對象都有一個layout或reflow方法,每個渲染對象調用需要佈局的children的layout方法。
(JavaScript和Style可以看做dom解析和樣式表解析併合並render tree的過程)
減少layout(這個應該就是重排迴流動作),paint(這個當重新渲染的時候就對應重繪操作),就儘量應該讓屬性引起的變化在Composite過程完成,也就是組合層,也叫渲染層合併,按照合理的順序合併圖層然後顯示到屏幕上。

利用 GPU 加速優先使用渲染層合併屬性,避免 layout,paint。

例如一個網頁的加載過程:
在這裏插入圖片描述

可以在上面看到,transform這個屬性過程是在Composite過程完成,相當於自己提升(創建)了一個圖層,最後合併,沒有影響之前的圖層,不會引起layout和paint了,這樣動畫效果更加流暢。

打開chrome工具rendering的layer Borders可以看到很多邊框和柵格,

黃色邊框:有動畫 3d 變換的元素,表示放到了一個新的複合層(composited layer)中渲染

藍色的柵格:這些分塊可以看作是比層更低一級的單位,這些區域就是 RenderLayer

打開一個頁面,如果該頁面的黃色邊框很多,那麼肯定要查看一下原因了

打開csstrigers網站,查詢transform,可以看到
在這裏插入圖片描述
在這裏插入圖片描述
在Blink Chrome瀏覽器和WebKit火狐瀏覽器上都是在Composite上完成的。“更改變換不會觸發任何幾何更改或繪製,這非常好。這意味着該操作可能由合成器線程在GPU的幫助下執行。”

參考:https://aotu.io/notes/2017/04/11/GPU/index.html

5、CSS會不會阻塞DOM解析/DOM渲染/瀏覽器渲染?

這個問題在實際表現中其實複雜,參考:鏈接

這位博主也只是列出了幾種情況下的表現,總結爲:

  1. CSS加載不阻塞DOM解析。DOM解析和CSS解析是兩個並行的進程。

  2. CSS加載會阻塞DOM的渲染。由於Render Tree是依賴於DOM Tree和CSSOM Tree的,所以他必須等待到CSSOM Tree構建完成,也就是CSS資源加載完成(或者CSS資源加載失敗)後,才能開始渲染。

  3. CSS會阻塞後面js的執行。由於js可能會操作之前的Dom節點和css樣式,因此瀏覽器會維持html中css和js的順序。因此,樣式表會在後面的js執行前先加載執行完畢。所以css會阻塞後面js的執行。

寫到這裏,就不得不提下瀏覽器中的一個生命週期事件:DOMContentLoaded

6、關於DOMContentLoaded

首先看看MDN上的定義:

當初始的 HTML 文檔被完全加載和解析完成之後,DOMContentLoaded 事件被觸發,而無需等待樣式表、圖像和子框架的完成加載。另一個不同的事件 load 應該僅用於檢測一個完全加載的頁面。 在使用 DOMContentLoaded 更加合適的情況下使用 load 是一個令人難以置信的流行的錯誤,所以要謹慎。注意:DOMContentLoaded 事件必須等待其所屬script之前的樣式表加載解析完成纔會觸發。

看看,一下子就把上面聯繫起來了,舉了一個例子:
在這裏插入圖片描述
如果script前有CSS的link,那麼這個事件就會在3秒css解析完纔會觸發執行。如果link放在之後,就會立即執行打印。

解釋:
因爲js會阻塞DOM解析,DOMContentLoaded是在DOM解析完成後才觸發。因此,當css後面有js的時候,css會阻塞js運行,而js會阻塞DOM解析,從而導致DOMContentLoaded必須等到css以及css後面的js執行完成後,纔會觸發。(而當css後面沒有js的時候,由於css不阻塞DOM的解析,因此DOMContentLoaded不會等待css的加載。)

需要注意的是,現在我們一般把script放在body的最後,如果這塊的script含有對DOMContentLoaded事件的監聽回調函數,那麼就不合理了,因爲放在最後的這塊一定會被head的CSS link所阻塞。解決方法是把DOMContentLoaded監聽回調函數單獨放在一個script放在head的所有link CSS之前。或者用新H5 API defer和asyc script解決(未驗證)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章