當渲染對象被建立并添加到樹中,它們并沒有位置和大小,計算這些值的過程稱為layout或reflow。
Html使用基于流的布局模型,意味着大部分時間,可以以單一的途徑進行幾何計算。流中靠後的元素并不會影響前面元素的幾何特性,是以布局可以在文檔中從右向左、自上而下的進行。也存在一些例外,比如html tables。
坐标系統相對于根frame,使用top和left坐标。
布局是一個遞歸的過程,由根渲染對象開始,它對應html文檔元素,布局繼續遞歸的通過一些或所有的frame層級,為每個需要幾何資訊的渲染對象進行計算。
根渲染對象的位置是0,0,它的大小是viewport-浏覽器視窗的可見部分。
所有的渲染對象都有一個layout或reflow方法,每個渲染對象調用需要布局的children的layout方法。
Dirty bit系統
為了不因為每個小變化都全部重新布局,浏覽器使用一個dirty bit系統,一個渲染對象發生了變化或是被添加了,就标記它及它的children為dirty——需要layout。存在兩個辨別——dirty及children are dirty,children are dirty說明即使這個渲染對象可能沒問題,但它至少有一個child需要layout。
全局和增量layout
當layout在整棵渲染樹觸發時,稱為全局layout,這可能在下面這些情況下發生:
1. 一個全局的樣式改變影響所有的渲染對象,比如字号的改變。
2. 視窗resize。
layout也可以是增量的,這樣隻有标志為dirty的渲染對象會重新布局(也将導緻一些額外的布局)。增量layout會在渲染對象dirty時異步觸發,例如,當網絡接收到新的内容并添加到Dom樹後,新的渲染對象會添加到渲染樹中。
圖20:增量layout
異步和同步layout
增量layout的過程是異步的,Firefox為增量layout生成了reflow隊列,以及一個排程執行這些批處理指令。WebKit也有一個計時器用來執行增量layout-周遊樹,為dirty狀态的渲染對象重新布局。
另外,當腳本請求樣式資訊時,例如“offsetHeight”,會同步的觸發增量布局。
全局的layout一般都是同步觸發。
有些時候,layout會被作為一個初始layout之後的回調,比如滑動條的滑動。
優化
當一個layout因為resize或是渲染位置改變(并不是大小改變)而觸發時,渲染對象的大小将會從緩存中讀取,而不會重新計算。
一般情況下,如果隻有子樹發生改變,則layout并不從根開始。這種情況發生在,變化發生在元素自身并且不影響它周圍元素,例如,将文本插入文本域(否則,每次擊鍵都将觸發從根開始的重排)。
layout過程
layout一般有下面這幾個部分:
1. parent渲染對象決定它的寬度
2. parent渲染對象讀取chilidren,并:
a. 放置child渲染對象(設定它的x和y)
b. 在需要時(它們目前為dirty或是處于全局layout或者其他原因)調用child渲染對象的layout,這将計算child的高度
c. parent渲染對象使用child渲染對象的累積高度,以及margin和padding的高度來設定自己的高度-這将被parent渲染對象的parent使用
d. 将dirty辨別設定為false
Firefox使用一個“state”對象(nsHTMLReflowState)做為參數去布局(firefox稱為reflow),state包含parent的寬度及其他内容。
Firefox布局的輸出是一個“metrics”對象(nsHTMLReflowMetrics)。它包括渲染對象計算出的高度。
寬度計算
渲染對象的寬度使用容器的寬度、渲染對象樣式中的寬度及margin、border進行計算。例如,下面這個div的寬度:
<div />
webkit中寬度的計算過程是(RenderBox類的calcWidth方法):
- 容器的寬度是容器的可用寬度和0中的最大值,這裡的可用寬度為:contentWidth=clientWidth()-paddingLeft()-paddingRight(),clientWidth和clientHeight代表一個對象内部的不包括border和滑動條的大小
- 元素的寬度指樣式屬性width的值,它可以通過計算容器的百分比得到一個絕對值
- 加上水準方向上的border和padding
到這裡是最佳寬度的計算過程,現在計算寬度的最大值和最小值,如果最佳寬度大于最大寬度則使用最大寬度,如果小于最小寬度則使用最小寬度。最後緩存這個值,當需要layout但寬度未改變時使用。
Line breaking
當一個渲染對象在布局過程中需要折行時,則暫停并告訴它的parent它需要折行,parent将建立額外的渲染對象并調用它們的layout。