摘要:網(wǎng)頁的渲染方式主要有兩種軟件渲染和硬件加速渲染。而使用合成化的渲染技術(shù),以使用軟件繪圖的合成化渲染為例,對于使用繪制的層,其結(jié)果保存在內(nèi)存中,之后傳輸?shù)街羞M(jìn)行合成。
Webkit 渲染基礎(chǔ)與硬件加速
當(dāng)瀏覽器加載一個(gè) html 文件并對它進(jìn)行解析完畢后,內(nèi)核就會生成一個(gè)極為重要的數(shù)據(jù)結(jié)構(gòu)即 DOM 樹,樹上每一個(gè)節(jié)點(diǎn)都對應(yīng)著網(wǎng)頁里面的某一個(gè)元素,并且開發(fā)人員也可以通過 JavaScript 操作這棵 DOM 樹動態(tài)改變它的結(jié)構(gòu),但是 DOM 樹本身并不能直接用于排版和渲染。
瀏覽器中頁面的渲染過程可以簡化為以下五個(gè)步驟:
從 DOM 到 RenderObject在 DOM 樹構(gòu)建完成之后,Webkit 所要做的事情就是為 DOM 樹節(jié)點(diǎn)構(gòu)建 RenderObject 樹,一個(gè) RenderObject 對象保存為繪制 DOM 節(jié)點(diǎn)所需要的各種信息,從以下這些規(guī)則出發(fā)會為 DOM 樹節(jié)點(diǎn)創(chuàng)建 RenderObject 對象:
DOM 樹的 document 節(jié)點(diǎn)
DOM 樹中的可視節(jié)點(diǎn)
某些情況下 Webkit 需要建立匿名的 RenderObject 節(jié)點(diǎn),這樣的節(jié)點(diǎn)不對應(yīng)于 DOM 樹中的任何節(jié)點(diǎn),而是 Webkit 處理上的需要,典型的例子就是匿名用于表示塊元素的 RenderBlock 節(jié)點(diǎn)
雖然 Javascript 無法訪問影子節(jié)點(diǎn),但是需要為其創(chuàng)建并渲染 RenderObject
Tip:由此可見,網(wǎng)上有人說瀏覽器渲染的步驟中包含“將 DOM 樹和 CSSOM 樹合并為 Render 樹”的說法是有些問題的。CSSOM(CSS 對象模型)是用于提供方法可以讓開發(fā)者自定義一些腳本來操作其樣式狀態(tài)的,它的思想是在 DOM 中的一些節(jié)點(diǎn)接口中加入獲取和操作 CSS 屬性或者接口的 Javascript 接口,便于 Javascript 可以動態(tài)操作 CSS 樣式。
由此我們可以知道 RenderObject 樹和 DOM 樹不是一一對應(yīng)的!我們可以簡單的認(rèn)為,RenderObject 是銜接瀏覽器排版引擎和渲染引擎之間的橋梁,它是排版引擎的輸出和渲染引擎的輸入。當(dāng) Webkit 創(chuàng)建 RenderObject 對象之后,每個(gè)對象是不知道自己的位置、大小等信息的,Webkit 根據(jù)框模型來計(jì)算它們的位置、大小等信息的過程稱為布局計(jì)算。
Tip:從整個(gè)網(wǎng)頁的加載和渲染過程來看,CSS 解釋器和規(guī)則匹配處于 DOM 樹建立之后,RenderObject 樹建立之前,CSS 解釋器解釋后的結(jié)果會保存起來,然后 RenderObject 樹基于該結(jié)果來進(jìn)行規(guī)范匹配和布局計(jì)算。
既然已經(jīng)實(shí)現(xiàn)繪制每個(gè) DOM 節(jié)點(diǎn)的方法,那是不是可以開辟一段位圖空間,然后 DFS 遍歷這個(gè) RenderObject 樹執(zhí)行繪制方法,就像“蓋章”一樣把每個(gè) RenderObject 的內(nèi)容一個(gè)個(gè)的蓋到“畫布上”,是不是就足夠完成繪制?
如果沒有層疊上下文,到這兒就可以結(jié)束了!實(shí)際上是如果沒有 Positioning,Clipping,Overflow Scroll,CSS Transform/Opacity/Animation/Filter,Mask or Reflection,Z Indexing etc. 到這兒就可以結(jié)束了……
從 RenderObject 到 RenderLayerWebkit 會為網(wǎng)頁的層次創(chuàng)建相應(yīng)的 RenderLayer 對象,當(dāng)某些類型的 RenderObject 的節(jié)點(diǎn)或者具有某些 CSS 樣式的 RenderObject 節(jié)點(diǎn)出現(xiàn)的時(shí)候,Webkit 就會為這些節(jié)點(diǎn)創(chuàng)建 RenderLayer 對象,一般來說某個(gè) RenderObject 節(jié)點(diǎn)的后代都屬于該節(jié)點(diǎn)的 RenderLayer,除非 Webkit 根據(jù)規(guī)則為某個(gè)后代 RenderObject 節(jié)點(diǎn)創(chuàng)建一個(gè)新的 RenderLayer 對象,以下是 RenderObject 節(jié)點(diǎn)需要建立新的 RenderLayer 節(jié)點(diǎn)的規(guī)則:
DOM 樹的 document 節(jié)點(diǎn)對應(yīng)的 RenderView 節(jié)點(diǎn)
DOM 樹中 document 的子女節(jié)點(diǎn),即 html 節(jié)點(diǎn)對應(yīng)的 RenderBlock 節(jié)點(diǎn)
顯示指定 CSS 位置的 RendrObject 節(jié)點(diǎn)
有透明效果的 RenderObject 節(jié)點(diǎn)
節(jié)點(diǎn)有溢出(overflow)、alpha 或者反射等效果的 RenderObject 節(jié)點(diǎn)
適用 canvas 2d 或者 3d(WebGL)技術(shù)的 RenderObject 節(jié)點(diǎn)
video 節(jié)點(diǎn)對應(yīng)的 RenderObject 節(jié)點(diǎn)
由此我們可以知道,RenderLayer 節(jié)點(diǎn)和 RenderObject 節(jié)點(diǎn)不是一一對應(yīng)的,而是一對多的關(guān)系。
具體來說,根據(jù)創(chuàng)建 RenderLayer 的原因不同可以將其分為常見的 3 類:
NormalPaintLayer根元素(html)
有明確的定位屬性(relative、fixed、sticky、absolute)
透明的(opacity 小于 1)
有 CSS 濾鏡(fliter)
有 CSS mask 屬性
有 CSS mix-blend-mode 屬性(不為 normal)
有 CSS transform 屬性(不為 none)
backface-visibility 屬性為 hidden
有 CSS reflection 屬性
有 CSS column-count 屬性(不為 auto)
有 CSS column-width 屬性(不為 auto)
當(dāng)前有對于 opacity、transform、fliter、backdrop-filter 的應(yīng)用動畫
OverflowClipPaintLayeroverflow 不為 visible
NoPaintLayer不需要 paint 的 RenderLayer:比如一個(gè)沒有視覺屬性(背景、顏色、陰影等)的空 div
上文中講解的從 DOM 到 RenderObject 以及從 RenderObject 到 RenderLayer 可以歸納如下圖:
軟件渲染和硬件加速渲染在 Webkit 中繪圖操作被定義為一個(gè)抽象層即繪圖上下文,所有繪圖操作都是在該上下文中進(jìn)行,可以分為兩種類型:2d 圖形上下文和 3d 圖形上下文。其中 2d 圖形上下文的具體作用就是提供基本繪圖單元的繪制接口以及設(shè)置繪圖的樣式,繪圖接口包括畫點(diǎn)、畫線、畫圖片、畫多邊形、畫文字 etc.,繪圖樣式包括顏色、線寬、字號大小、漸變 etc.,而RenderObject 對象知道自己需要畫什么樣的點(diǎn),什么樣的圖片。3d 繪圖上下文的主要用處是支持 CSS3D、WebGL etc.。
網(wǎng)頁的渲染方式主要有兩種:軟件渲染和硬件加速渲染。每個(gè) RenderLyaer 對象都可以被想象成一個(gè)層,各個(gè)層一同構(gòu)成一個(gè)圖像,在渲染過程中,每個(gè)層對應(yīng)網(wǎng)頁中的一個(gè)或者一些可視元素,這些元素都繪制內(nèi)容到該層上,如果這些繪圖操作由 CPU 萊完成則稱之為軟件繪圖,如果這些繪圖操作由 GPU 來完成則稱之為硬件加速繪圖。理想情況下,每個(gè)層都有繪制的存儲區(qū)域來保存繪圖的結(jié)果,最后需要將這些層的內(nèi)容合并到同一個(gè)圖像中的過程稱為合成(compositing),使用合成技術(shù)的渲染叫做合成化渲染。
對于軟件渲染機(jī)制,Webkit 需要使用 CPU 來繪制每層的內(nèi)容,然而該機(jī)制是沒有合成階段的:在軟件渲染中通常其結(jié)果就是一個(gè)位圖(Bitmap),繪制每一層時(shí)都使用同一個(gè)位圖,區(qū)別在于繪制的位置看你不一樣,每一層都按照從后到前的順序。而使用合成化的渲染技術(shù),以使用軟件繪圖的合成化渲染為例,對于使用 CPU 繪制的層,其結(jié)果保存在 CPU 內(nèi)存中,之后傳輸?shù)?GPU 中進(jìn)行合成。
對于常見的 2d 繪圖操作,使用 GPU 來繪圖不一定比使用 CPU 繪圖在性能上有優(yōu)勢,原因是 CPU 使用緩存機(jī)制有效減少重復(fù)繪制得開銷,而且不需要 GPU 的并行,并且 GPU 的內(nèi)存資源相對 CPU 的內(nèi)存資源更加緊張。什么是位圖
在繪制出一個(gè)圖片我們應(yīng)該怎么做,顯然首先是把這個(gè)圖片表示為一種計(jì)算機(jī)能理解的數(shù)據(jù)結(jié)構(gòu):用一個(gè)二維數(shù)組,數(shù)組的每個(gè)元素記錄這個(gè)圖片中的每一個(gè)像素的具體顏色。所以瀏覽器可以用位圖來記錄它想在某個(gè)區(qū)域繪制的內(nèi)容,繪制的過程也就是往數(shù)組中具體的下標(biāo)里填寫像素而已。
什么是紋理紋理其實(shí)就是 GPU 中的位圖,存儲在 GPU video RAM 中。前面說的位圖里的元素存什么我們自己定義好就行(是用3字節(jié)存256位rgb還是1個(gè)bit存黑白自己定義即可),但紋理是 GPU 專用的,需要有固定格式便于兼容與處理,所以一方面紋理的格式比較固定,如 R5G6B5、A4R4G4B4 等像素格式, 另外一方面 GPU 對紋理的大小有限制,比如長/寬必須是2的冪次方,最大不能超過2048或者4096等。
什么是光柵化在紋理里填充像素不是那么簡單的自己去遍歷位圖里的每個(gè)元素然后填寫這個(gè)像素的顏色即可,光柵化的本質(zhì)是坐標(biāo)變換、幾何離散化,然后再填充。
同時(shí),光柵化從早期的 Full-screen Rasterization 基本都進(jìn)化到現(xiàn)在的 Tile-Based Rasterization,也就是不對整個(gè)圖像做光柵化,而是把圖像分塊后再對每個(gè) tile 多帶帶光柵化。光柵化完成后將像素填充進(jìn)紋理,再將紋理上傳至 GPU,
原因一方面如上文所說,紋理大小有限制,即使整屏光柵化也是要填進(jìn)小塊小塊的紋理中,不如事先根據(jù)紋理大小分塊光柵化后再填充進(jìn)紋理里;另一方面是為了減少內(nèi)存占用(整屏光柵化意味著需要準(zhǔn)備更大的buffer空間)和降低總體延遲(分塊柵格化意味著可以多線程并行處理)。
非合成加速的渲染架構(gòu),所有的 RenderLayer 都沒有自己獨(dú)立的緩存,它們都按照先后順序被繪制到同一個(gè)緩存里面,所以只要這個(gè) RenderLayer 觸發(fā)重繪,變化區(qū)域的緩存就需要重新生成,此時(shí)不但需要繪制發(fā)生變化的 RenderLayer,跟變化區(qū)域(Damage Region)相交的其它 RenderLayer 也需要被繪制。
瀏覽器本身并不能直接改變屏幕的像素輸出,它需要通過系統(tǒng)本身的 GUI Toolkit,所以一般來說瀏覽器會將一個(gè)要顯示的網(wǎng)頁包裝成一個(gè) UI 組件,通常叫做 WebView,然后通過將 WebView 放置于應(yīng)用的 UI 界面上,從而將網(wǎng)頁顯示在屏幕上。
默認(rèn)的情況下 UI 組件沒有自己獨(dú)立的位圖緩存,構(gòu)成 UI 界面的所有 UI 組件都直接繪制在當(dāng)前的窗口緩存上,所以 WebView 每次繪制就相當(dāng)于將它在可見區(qū)域內(nèi)的 RenderLayer/RenderObject 逐個(gè)繪制到窗口緩存上。上述的渲染方式有一個(gè)很嚴(yán)重的問題,用戶拖動網(wǎng)頁或者觸發(fā)一個(gè)慣性滾動時(shí),網(wǎng)頁滑動的渲染性能會十分糟糕:這是因?yàn)榧词咕W(wǎng)頁只移動一個(gè)像素,整個(gè) WebView 都需要重新繪制。
要提升網(wǎng)頁滑屏的性能,一個(gè)簡單的做法就是讓 WebView 本身持有一塊獨(dú)立的緩存,而 WebView 的繪制就分成了兩步: 1) 根據(jù)需要更新內(nèi)部緩存,將網(wǎng)頁內(nèi)容繪制到內(nèi)部緩存里面 2) 將內(nèi)部緩存拷貝到窗口緩存上。第一步我們通常稱為繪制(Paint)或者光柵化(Rasterization),它將一些繪圖指令轉(zhuǎn)換成真正的像素顏色值,而第二步我們一般稱為合成(Composite),它負(fù)責(zé)緩存的拷貝,同時(shí)還可能包括位移(Translation),縮放(Scale),旋轉(zhuǎn)(Rotation),Alpha 混合等操作。
從 RenderLayer 到 GraphicsLayer在現(xiàn)實(shí)情況中,由于硬件能力和資源有限,為了節(jié)省 GPU 的內(nèi)存資源,硬件加速機(jī)制在 RenderLayer 樹建立之后需要做三件事情來完成網(wǎng)頁的渲染:
Webkit 決定將哪些 RendeLayer 對象組合在一起,形成一個(gè)由后端存儲(一般指 GPU 內(nèi)存)的新層,對于一個(gè) RenderLayer 對象來說,如果它沒有后端存儲的新層,那么就使用其父親所使用的合成層
將每個(gè)合成層包含的 RenderLayer 內(nèi)容繪制在其后端存儲中,這里的繪制可以是軟件繪制,也可以是硬件加速繪制
由合成器將多個(gè)合成層合成起來,形成網(wǎng)頁的最終可視化結(jié)果(實(shí)際就是一張圖片)
一個(gè) RenderLayer 對象如果需要后端存儲,它會創(chuàng)建一個(gè) RenderLayerBacking 對象,負(fù)責(zé) RenderLayer 對象所需要的各種存儲,每個(gè) RenderLayer 對象都可以創(chuàng)建自己的后端存儲,然而不是所有 RenderLayer 對象都有自己的 RenderLayerBacking,如果一個(gè) RenderLayer 對象被 Webkit 按照一定的規(guī)則創(chuàng)建后端存儲,那么該層被稱為合成層,后端存儲可能需要管理多個(gè)存儲空間,使用 GraphicsLayer 類來表示。
每個(gè) GraphicsLayer 都擁有一個(gè) GraphicsContext,用于為該 GraphicsLayer 開辟一段位圖,也就意味著每個(gè) GraphicsLayer 都擁有一個(gè)獨(dú)立的位圖,GraphicsLayer 負(fù)責(zé)將自己的 RenderLayer 及其所包含的 RenderObject 繪制到位圖里,然后將位圖作為紋理交給 GPU 進(jìn)行合成。如果一個(gè) RenderLayer 對象具有以下特征之一,那么它就是合成層:
RenderLayer 具有 CSS3D 屬性或者 CSS 透視效果
RenderLayer 包含 video 節(jié)點(diǎn)對應(yīng)的 RenderObject 節(jié)點(diǎn)
RenderLayer 包含使用 canvas 2d 或者 3d(WebGL)技術(shù)的 RenderObject 節(jié)點(diǎn)
RenderLayer 使用 CSS 透明效果的動畫或者 CSS 變換動畫
RenderLayer 使用硬件加速的 CSS Filters 技術(shù)
RenderLayer 使用裁剪或者反射屬性,并且其后代包含合成層
RenderLayer 有一個(gè) Z 坐標(biāo)比自己小的兄弟節(jié)點(diǎn),且該兄弟節(jié)點(diǎn)是一個(gè)合成層
直接原因硬件加速的 iframe 元素(比如 iframe 嵌入的頁面中有合成層)
video 元素
覆蓋在 video 元素上的視頻控制欄
3D 或者 硬件加速的 2D Canvas 元素
硬件加速的插件:比如 flash etc.
在 DPI 較高的屏幕上 fix 定位的元素會自動地被提升到合成層中;但在 DPI 較低的設(shè)備上卻并非如此:因?yàn)檫@個(gè)渲染層的提升會使得字體渲染方式由子像素變?yōu)榛译A
有 3D transform
backface-visibility 為 hidden
對 opacity、transform、fliter、backdropfilter 應(yīng)用 animation 或者 transition(需要是 active 的 animation 或者 transition,當(dāng) animation 或者 transition 效果未開始或結(jié)束后,提升合成層也會失效)
will-change 設(shè)置為 opacity、transform、top、left、bottom、right(其中 top、left 等需要設(shè)置明確的定位屬性:比如 relative etc.)
后代元素原因有合成層后代同時(shí)本身有 transform、opactiy(小于 1)、mask、fliter、reflection 屬性
有合成層后代同時(shí)本身 overflow 不為 visible(如果本身是因?yàn)槊鞔_的定位因素產(chǎn)生的 SelfPaintingLayer,則需要 z-index 不為 auto)
有合成層后代同時(shí)本身 fixed 定位
有 3D transfrom 的合成層后代同時(shí)本身有 preserves-3d 屬性
有 3D transfrom 的合成層后代同時(shí)本身有 perspective 屬性
重疊原因重疊或者說部分重疊在一個(gè)合成層之上,最常見和容易理解的就是元素的 border box(content + padding + border) 和合成層的有重疊,其他的還有一些不常見的情況,也算是同合成層重疊的條件如下:
filter 效果同合成層重疊
transform 變換后同合成層重疊
overflow scroll 情況下同合成層重疊
假設(shè)重疊在一個(gè)合成層之上,其實(shí)也比較好理解,比如一個(gè)元素的 CSS 動畫效果在運(yùn)行期間,元素有可能和其他元素發(fā)生重疊的情況,需要注意的是該原因下,有一個(gè)很特殊的情況:如果合成層有內(nèi)聯(lián)的 transform 屬性,會導(dǎo)致其兄弟渲染層假設(shè)重疊從而提升為合成層。
基本上常見的一些合成層的提升原因如上所說,我們會發(fā)現(xiàn):由于重疊的原因可能隨隨便便就會產(chǎn)生出大量合成層來,而每個(gè)合成層都要消耗 CPU 和內(nèi)存資源,豈不是嚴(yán)重影響頁面性能?!
層壓縮這一點(diǎn)瀏覽器也考慮到,因此就有層壓縮(Layer Squashing)的處理。如果多個(gè)渲染層同一個(gè)合成層重疊時(shí),這些渲染層會被壓縮到一個(gè) GraphicsLayer 中,以防止由于重疊原因?qū)е驴赡艹霈F(xiàn)的“層爆炸”。
當(dāng)然,瀏覽器的自動層壓縮也不是萬能的,在很多特定情況下,瀏覽器是無法進(jìn)行層壓縮的,而這些情況也是我們應(yīng)該盡量避免的(以下情況都是基于重疊原因而言):
無法進(jìn)行會打破渲染順序的壓縮
video 元素的渲染層無法被壓縮,同時(shí)也無法將別的渲染層壓縮到 video 所在的合成層上
iframe、plugin 的渲染層無法被壓縮,同時(shí)也無法將別的渲染層壓縮到其所在的合成層上
無法壓縮有 reflection 屬性的渲染層
無法壓縮有 blend mode 屬性的渲染層
當(dāng)渲染層同合成層有不同的裁剪容器時(shí),該渲染層無法壓縮
相對于合成層滾動的渲染層無法被壓縮
當(dāng)渲染層同合成層有不同的具有 opacity 的祖先層(一個(gè)設(shè)置 opacity 且小于 1 一個(gè)沒有設(shè)置 opacity 也算是不同)時(shí),該渲染層無法壓縮
當(dāng)渲染層同合成層有不同的具有 transform 的祖先層時(shí),該渲染層無法壓縮
當(dāng)渲染層同合成層有不同的具有 filter 的祖先層時(shí),該渲染層無法壓縮
當(dāng)覆蓋的合成層正在運(yùn)行動畫時(shí),該渲染層無法壓縮,只有在動畫未開始或者運(yùn)行完畢以后,該渲染層才可以被壓縮
多線程進(jìn)一步來說,瀏覽器還可以使用多線程的渲染架構(gòu),將網(wǎng)頁內(nèi)容繪制到緩存的操作放到另外一個(gè)獨(dú)立的線程(繪制線程),而原來線程對 WebView 的繪制就只剩下緩存的拷貝(合成線程),繪制線程跟合成線程之間可以使用同步,部分同步,完全異步等作業(yè)模式,讓瀏覽器可以在性能與效果之間根據(jù)需要進(jìn)行選擇。
Main thread or WebKit/Blink thread內(nèi)核線程 - 負(fù)責(zé)解析,排版,Render 樹繪制,JavaScript 執(zhí)行等任務(wù),它有可能執(zhí)行真正的網(wǎng)頁內(nèi)容的光柵化,也有可能只是紀(jì)錄繪制指令,由獨(dú)立的光柵化線程執(zhí)行
Rasterize thread光柵化線程 - 如果內(nèi)核線程只負(fù)責(zé)將網(wǎng)頁內(nèi)容轉(zhuǎn)換為繪圖指令列表,則真正的光柵化(執(zhí)行繪圖指令計(jì)算出像素的顏色值)由獨(dú)立的光柵化線程完成
Compositor thread合成線程 - 負(fù)責(zé)將網(wǎng)頁內(nèi)部位圖緩存/紋理輸出到窗口的幀緩存,從而把網(wǎng)頁顯示在屏幕上,但是在使用 GPU 合成的情況下,也有可能只是產(chǎn)生 GL 繪圖指令,然后將繪圖指令的緩存發(fā)送給 GPU 線程執(zhí)行
GPU threadGPU 線程 - 如果使用 GPU 合成,則由 GPU 線程負(fù)責(zé)執(zhí)行 GL 繪圖指令,訪問 GPU,可能跟合成線程是同一個(gè)線程,也有可能是獨(dú)立的線程(合成線程產(chǎn)生GL指令 GPU 線程執(zhí)行)
Browser UI thread瀏覽器 UI 線程,如果跟 GPU 線程不是同一個(gè)線程,則只負(fù)責(zé)外殼的繪制,如果跟 GPU 線程是同一個(gè)線程,則同時(shí)負(fù)責(zé)繪制外殼的UI界面,和網(wǎng)頁的合成輸出,到窗口幀緩存
重排&重繪重排和重繪是老生常談的東西,大家也應(yīng)該非常熟悉,但在這里可以結(jié)合瀏覽器機(jī)制順帶講一遍。
重排首先,如果你改變一個(gè)影響元素布局信息的 CSS 樣式:比如 width、height、left、top etc.(transform除外),那么瀏覽器會將當(dāng)前的 Layout 標(biāo)記為 dirty,這會使得瀏覽器在下一幀執(zhí)行重排,因?yàn)樵氐奈恢眯畔l(fā)生改變將可能會導(dǎo)致整個(gè)網(wǎng)頁其他元素的位置情況都發(fā)生改變,所以需要執(zhí)行 Layout 全局重新計(jì)算每個(gè)元素的位置。
需要注意到,瀏覽器是在下一幀、下一次渲染的時(shí)候才重排,并不是 JS 執(zhí)行完這一行改變樣式的語句之后立即重排,所以你可以在 JS 語句里寫 100 行修改 CSS 的語句,但是只會在下一幀的時(shí)候重排一次。
會觸發(fā)重排的屬性和方法如下:
clientHeight, clientWidth, clientTop, clientLeft, focus(), getBoundingClientRect(), getClientRects(), innerText, offsetHeight, offsetLeft, OffsetParent, offsetTop, offsetWidth, outerText, scrollByLines(), scrollByPages(), scrollHeight, scrollIntoView(), scrollIntoViewIfNeeded(), scrollLeft, scrollTop, scrollWidth
Frame, Imageheight, width
RangegetBoundingClientRect(), getClientRects()
SVGLocatablecomputeCTM(), getBBox()
SVGTextContentgetCharNumAtPosition(), getComputedTextLength(), getEndPositionOfChar(), getExtentOfChar(), getNumberOfChars(), getRotationOfChar(), getStartPositionOfChar(), getSubStringLength(), selectSubString()
SVGUseinstanceRoot
windowgetComputedStyle(), scrollBy(), scrollTo(), scrollX, scrollY, webkitConvertPointFromNodeToPage(), webkitConvertPointFromPageToNode()
強(qiáng)制重排如果你在當(dāng)前 Layout 被標(biāo)記為 dirty 的情況下訪問 offsetTop、scrollHeight 等屬性,那么瀏覽器會立即重新 Layout,計(jì)算出此時(shí)元素正確的位置信息,以保證你在 JS 里獲取到的 offsetTop、scrollHeight 等是正確的。
這一過程被稱為強(qiáng)制重排 Force Layout,強(qiáng)制瀏覽器將本來在渲染流程中才執(zhí)行的 Layout 過程提前至 JS 執(zhí)行過程中,每次當(dāng)我們在 Layout 為 dirty 時(shí)訪問會觸發(fā)重排的屬性都會 Force Layout,這會極大延緩 JS 的執(zhí)行效率。
另外,每次重排或者強(qiáng)制重排后,當(dāng)前 Layout 就不再 dirty,這時(shí)再訪問 offsetWidth 之類的屬性并不會再觸發(fā)重排。
重繪重繪也是相似的,一旦你更改某個(gè)元素的會觸發(fā)重繪的樣式,那么瀏覽器就會在下一幀的渲染步驟中進(jìn)行重繪(也即一些介紹重繪機(jī)制中說的 invalidating),JS 更改樣式導(dǎo)致某一片區(qū)域的樣式作廢,從而在一下幀中重繪 invalidating 的區(qū)域。
但是!有一個(gè)非常關(guān)鍵的行為就是:重繪是以合成層為單位的,也即 invalidating 的既不是整個(gè)文檔也不是單個(gè)元素,而是這個(gè)元素所在的合成層。當(dāng)然這也是將渲染過程拆分為 Paint 和 Compositing 的初衷之一:
Since painting of the layers is decoupled from compositing, invalidating one of these layers only results in repainting the contents of that layer alone and recompositing.使用 transform 或者 opacity 來實(shí)現(xiàn)動畫效果
修改一些 CSS 屬性如 width、float、border、position、font-size、text-align、overflow-y etc. 會觸發(fā)重排、重繪和合成,修改另一些屬性如 color、background-color、visibility、text-decoration etc. 則不會觸發(fā)重排,只會重繪和合成。
接下來很多文章里就會說,修改 opacity、transform 這兩個(gè)屬性僅僅會觸發(fā)合成,不會觸發(fā)重繪,所以一定要用這兩個(gè)屬性來實(shí)現(xiàn)動畫,沒有重繪重排,效率很高……然而事實(shí)并不是這樣,只有一個(gè)元素在被提升為合成層之后,上述情況才成立。最后一句話:合成層提升并非銀彈!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/93297.html
摘要:書接上文瀏覽器內(nèi)核之渲染基礎(chǔ)硬件加速基礎(chǔ)概念硬件加速技術(shù)是指使用的硬件能力為幫助渲染網(wǎng)頁,在為的作用主要是用來繪制圖形并且性能特別好。包含的節(jié)點(diǎn)表示的是使用硬件加速的元素或者技術(shù)。 showImg(https://segmentfault.com/img/remote/1460000016348971); 微信公眾號:愛寫bugger的阿拉斯加如有問題或建議,請后臺留言,我會盡力解決你...
摘要:多線程的主要目的就是為了保持用戶界面的高響應(yīng)度,保證線程進(jìn)程中的主線程不會被任何其他費(fèi)用時(shí)的操作阻礙從而影響了對用戶操作的響應(yīng)。 showImg(https://segmentfault.com/img/remote/1460000016113034); 微信公眾號:愛寫bugger的阿拉斯加如有問題或建議,請后臺留言,我會盡力解決你的問題。 前言 此文章是我最近在看的【W(wǎng)ebKit ...
摘要:避免在頁面的主體布局中使用,要等其中的內(nèi)容完全下載之后才會顯示出來,顯示比布局慢。實(shí)現(xiàn)多行文本溢出顯示效果實(shí)現(xiàn)方法適用范圍因使用了的擴(kuò)展屬性,該方法適用于瀏覽器及移動端 在過去的一年很多人不滿于公司沒有福利、人際關(guān)系不好相處、沒有發(fā)展前途的境遇等等,想著在開年來換一份工作來重新開始自己,那么 你 準(zhǔn)備好了嗎? 下面是本人整理的一份面試材料,本想自己用的,但是新年第一天 公司突然給了我個(gè)...
摘要:避免在頁面的主體布局中使用,要等其中的內(nèi)容完全下載之后才會顯示出來,顯示比布局慢。實(shí)現(xiàn)多行文本溢出顯示效果實(shí)現(xiàn)方法適用范圍因使用了的擴(kuò)展屬性,該方法適用于瀏覽器及移動端 在過去的一年很多人不滿于公司沒有福利、人際關(guān)系不好相處、沒有發(fā)展前途的境遇等等,想著在開年來換一份工作來重新開始自己,那么 你 準(zhǔn)備好了嗎? 下面是本人整理的一份面試材料,本想自己用的,但是新年第一天 公司突然給了我個(gè)...
閱讀 798·2023-04-26 03:04
閱讀 2873·2021-11-15 18:10
閱讀 1201·2021-09-03 10:28
閱讀 1138·2019-08-30 15:53
閱讀 899·2019-08-30 12:45
閱讀 1968·2019-08-30 11:03
閱讀 2871·2019-08-29 14:01
閱讀 2935·2019-08-28 18:24