為什麼有些動畫會讓網頁卡頓
瀏覽器渲染網頁的流程(Rendering Pipeline):
- JavaScript 執行(影響 DOM 和 CSSOM)
- Style:計算每個元素應用的 CSS 規則
- Layout:計算每個元素的位置和大小(幾何計算)
- Paint:填充像素顏色(繪製)
- 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 動畫。