跳至主要内容

為什麼實作 CSS 動畫位移效果使用 translate()absolute 絕對定位更好?

剛好在最近工作上有遇到這個問題,在這裡我們會討論到 translate()absolute 的差異,以及為什麼我們應該使用 translate() 來移動元素。

translate()的優勢

translate() 是 CSS transform 的一個屬性,用來移動元素的位置。它是一個 2D 或 3D 的函數,可以在 x, y, z 軸上移動元素。

.element {
position: relative;
transform: translate(100px, 100px);
}

當使用 translate() 移動元素的時候,元素仍然保持在原本的位置,只是視覺上移動了,瀏覽器不會重新計算元素的佈局,也就是說,不會觸發 reflow。取而代之的是,translate() 只會觸發瀏覽器的 合成階段(compositing),這是在 GPU 而非主執行緒中執行的操作,因此效能更佳,並能產生流暢的動畫效果。

absolute 絕對定位的問題

絕對定位會將元素超脫原來的頁面流,並且會根據最近的父元素或是 body 來定位。

.element {
position: absolute;
top: 100px;
left: 100px;
}

使用 absolute 絕對定位該元素然後透過 topleftbottomright 來移動元素,但是當元素被移動後會觸發 reflow,這樣會造成效能問題,這個過程會導致瀏覽器重新繪製 (repaint) 整個畫面或部分畫面,從而增加效能負擔,尤其在複雜的網頁中,這可能會導致畫面卡頓或掉幀。

瀏覽器的渲染過程

這要從瀏覽器如渲染出一個網頁開始:

(一)、解析階段

  1. 瀏覽器會解析 HTML 並建立 DOM Tree
  2. 瀏覽器會解析 CSS Link tag 並建立 CSSOM Tree

(二)、Render 階段

  1. 在主執行緒中,DOM Tree 和 CSSOM Tree 會合併成 Render Tree。

  2. Layout: 在主執行緒中,瀏覽器根據 Render Tree 生成 Layout Tree,計算每個元素的位置和大小。

    • 這個過程瀏覽器會將頁面進行分層產生 Layer tree,每一層都是一個獨立的圖層。
    • 在這個階段瀏覽器會計算每個圖層的位置和大小,產生一個繪製的指令與塗層疊加的順序。
    • 最終這些圖層疊加在一起會形成頁面。
    • Reflow 回流:當元素的幾何位置和大小發生變化時,瀏覽器會重新計算 Layout Tree中的元素物理屬性,這個過程稱為 Reflow,例如: height、padding、position...等。
  3. Paint: 在主執行緒中,瀏覽器根據 Layout Tree 進行繪製。

    • Repaint 重繪:當元素的外觀發生變化時,瀏覽器會重新繪製元素,這個過程稱為 Repaint,例如: color、background-color...等。
  4. 合成 Compositing:在 Compositor thread 和 Raster thread 中,瀏覽器將繪製好的圖層進行合成。

    • 各個 Layer 圖層會進行柵格化 rasterize,將圖層轉換成 Pixel 顯示在螢幕上。
    • 在 Compositor thread 將這些經過柵格化的圖層進行合成,將這些圖層合成一個整體的畫面。
    • Compositor thread 會將 layer 切成一個個的 tile,然後將這些 tile 送到 Raster thread,將tile 進行柵格化後存到瀏覽器的 GPU 。
    • Compositor thread 根據 draw quads 資訊產生 Compositor frame,然後將這些 Compositor frame 送到 browser 階段 。

(三) Browser 階段

  1. 瀏覽器將 Compositor frame 送到 GPU 。
  2. 瀏覽器將渲染好的畫面顯示在螢幕上。

無論是 Reflow 或 Repaint ,當觸發了上方渲染的某一個階段後,後續的階段也都會跟著被觸發。

總結

所以回到這個問題的本質,當使用 absolute 絕對定位移動元素時,會觸發 reflow,這時候瀏覽器會重新計算元素的位置然後產生新的 Layout Tree,接著進行 Repaint 和 Compositing。 但是使用 translate() 移動元素時,不會觸發 reflowrepaint,只會觸發合成階段。此外還有另外一個好處是合成階段不是在主執行緒中進行的,這樣就不會影響到主執行緒的效能,因此使用 translate() 移動元素的效果比 absolute 絕對定位更可以縮短瀏覽器繪製的時間,還能讓動畫在 GPU 上進行處理讓動畫更加流暢。