為什麼有些動畫會讓網頁卡頓

瀏覽器渲染網頁的流程(Rendering Pipeline):

  1. JavaScript 執行(影響 DOM 和 CSSOM)
  2. Style:計算每個元素應用的 CSS 規則
  3. Layout:計算每個元素的位置和大小(幾何計算)
  4. Paint:填充像素顏色(繪製)
  5. Composite:把多個圖層組合成最終畫面

問題:不同的 CSS 屬性改變時,會觸發不同階段的重新計算。

改變 width、height、margin、padding、top、left 等「幾何屬性」→ 觸發 Layout → Paint → Composite(最貴,最耗效能)

改變 background-color、color、border-color 等「視覺屬性」→ 觸發 Paint → Composite(較貴)

改變 transform、opacity → 只觸發 Composite(最便宜,GPU 加速)

這就是為什麼 transform 和 opacity 是「效能友善的動畫屬性」。

效能最佳化的核心原則:只動 transform 和 opacity

想讓一個元素移動:用 transform: translateX() / translateY(),而不是改變 left / right / margin。

想讓元素淡入淡出:用 opacity,而不是改變 visibility 或 display。

想讓元素縮放:用 transform: scale(),而不是改變 width / height。

想讓元素旋轉:用 transform: rotate()。

這四個 transform 操作加上 opacity,幾乎可以覆蓋你需要的大多數動畫效果,而且全部在 GPU 上執行,不影響 CPU 的佈局計算。

will-change 的正確使用

will-change CSS 屬性告訴瀏覽器「這個元素即將發生改變」,讓瀏覽器提前把它放到獨立的 GPU 圖層:

範例:

.animated-element {

will-change: transform, opacity;

}

注意:不要把 will-change 濫用到所有元素上。每個獨立 GPU 圖層都佔用記憶體,過度使用 will-change 反而降低效能。只在確實會動畫的元素上使用。

使用 CSS transition 還是 animation?

CSS transition:適合「從一個狀態平滑過渡到另一個狀態」,例如 hover 效果、表單元素 focus、選單展開/收合。

CSS animation(@keyframes):適合「循環播放」或「多步驟」的動畫,例如 loading 指示器、持續的背景效果。

兩者都應該優先使用 transform 和 opacity 屬性。

JavaScript 動畫的效能考量

如果用 JavaScript 控制動畫(例如 GSAP 或手動 requestAnimationFrame),幾個注意事項:

使用 requestAnimationFrame 而非 setTimeout/setInterval:requestAnimationFrame 與瀏覽器的繪製週期同步,確保動畫在下一幀渲染前執行,避免在錯誤的時機修改 DOM。

避免在動畫循環中讀取 Layout 屬性:讀取 offsetWidth、offsetHeight、getBoundingClientRect 等屬性會強制瀏覽器進行布局計算,在動畫循環中反覆讀取這些屬性(「Layout Thrashing」)是效能殺手。

CSS 動畫的 prefers-reduced-motion

有些用戶因為視覺敏感(例如前庭障礙)需要減少動態效果。CSS 的 prefers-reduced-motion 媒體查詢讓你根據用戶的系統設定調整:

範例:

@media (prefers-reduced-motion: reduce) {

.animated-element {

animation: none;

transition: none;

}

}

Tailwind CSS 也提供 motion-safe: 和 motion-reduce: 前綴,讓你更方便地在 class 中處理這個問題。

效能測試工具

Chrome DevTools 的 Performance 面板可以錄下動畫過程的每一幀,顯示哪些操作觸發了 Layout、Paint,以及哪些幀超過了 16.7ms(60fps 的每幀預算)。

紅色的長條代表 Jank(卡頓),目標是讓所有動畫幀都在 16.7ms 以內完成。

Tailwind CSS 中的動畫管理

在 Tailwind 中,使用 transition、duration、ease 工具類可以快速設定 CSS transition:

transition-all duration-300 ease-out hover:scale-105

transition-colors duration-200 hover:text-accent-400

animate-spin(loading spinner)

animate-pulse(skeleton 載入效果)

對於複雜的動畫序列,可以在 tailwind.config.ts 的 theme.animation 中定義自訂的 keyframes 動畫。