Web Animations API 與高效能動畫:GPU 加速與 composite 最佳化

性能优化(更新於 2026年6月6日)

瀏覽器渲染管線與動畫效能

瀏覽器渲染一幀需要經過:Style → Layout → Paint → Composite。動畫效能的關鍵在於跳過盡可能多的階段

屬性類型 觸發階段 效能
width/height/margin Layout → Paint → Composite
color/background Paint → Composite
transform/opacity 僅 Composite

只有 transformopacity 能跳過 Layout 和 Paint,直接在 GPU 的 Composite 層完成合成。


GPU 加速:composite 層的運作原理

提升為合成層的條件

以下情況瀏覽器會將元素提升為獨立的合成層(Compositing Layer):

  • transform: translateZ(0)will-change: transform
  • <video><canvas>、WebGL 內容
  • CSS animation/transition 作用於 transformopacity
  • position: fixed(部分瀏覽器)

合成層的代價

每個合成層 = 獨立的 GPU 紋理 = 顯示記憶體佔用

層過多 → 顯示記憶體壓力 → 反而降低效能

建議:合成層數量控制在 30 個以內,行動裝置更少。


will-change:正確使用與常見誤區

基本用法

.animating-element {
  will-change: transform, opacity;
}

最佳實務

/* 差:全域宣告,浪費資源 */
.card {
  will-change: transform;
}

/* 好:僅在互動前宣告,互動後移除 */
.card:hover {
  will-change: transform;
}
.card.is-dragging {
  will-change: transform;
}
// JS 動態控制 will-change
element.addEventListener('mouseenter', () => {
  element.style.willChange = 'transform';
});
element.addEventListener('transitionend', () => {
  element.style.willChange = 'auto';
});

原則:will-change 是「預告」而非「最佳化開關」,僅在即將發生動畫時設定,動畫結束後移除。


Web Animations API:element.animate()

基本語法

const animation = element.animate(
  [
    { transform: 'translateX(0px)', opacity: 1 },
    { transform: 'translateX(300px)', opacity: 0.5 }
  ],
  {
    duration: 600,
    easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
    fill: 'forwards',
    iterations: 1
  }
);

與 CSS Animations 對比

特性 CSS Animations Web Animations API
宣告方式 CSS @keyframes JS element.animate()
動態控制 難(需操作 class) 簡單(play()/pause()/reverse()
動態關鍵幀 不支援 支援執行時產生
Promise 整合 animation.finished/animation.ready
效能 相同 相同(同一引擎)
瀏覽器最佳化 可預解析 執行時最佳化

動畫控制 API

const anim = element.animate(keyframes, options);

anim.pause();
anim.play();
anim.reverse();
anim.finish();
anim.cancel();

anim.playbackRate = 2.0;
anim.currentTime = 300;

anim.finished.then(() => {
  console.log('動畫完成');
});

requestAnimationFrame 與動畫排程

為什麼不用 setTimeout/setInterval

// 差:與螢幕重新整理不同步,可能掉幀
setInterval(() => {
  element.style.transform = `translateX(${x}px)`;
}, 16);

// 好:與瀏覽器重新整理率同步
function animate(timestamp) {
  const progress = (timestamp - startTime) / duration;
  element.style.transform = `translateX(${targetX * progress}px)`;
  if (progress < 1) requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

rAF 與 WAAPI 的配合

// 複雜場景:WAAPI 處理簡單動畫,rAF 處理需要逐幀計算的動畫
const scrollAnim = element.animate(
  [{ transform: 'translateY(0)' }, { transform: 'translateY(-100px)' }],
  { duration: 300, fill: 'forwards' }
);

// 捲動驅動動畫仍需 rAF
let ticking = false;
window.addEventListener('scroll', () => {
  if (!ticking) {
    requestAnimationFrame(() => {
      element.style.transform = `translateY(${window.scrollY * 0.5}px)`;
      ticking = false;
    });
    ticking = true;
  }
});

Composite 層最佳化實戰

減少重繪區域

/* 差:整個容器重繪 */
.container {
  animation: pulse 1s infinite;
}
@keyframes pulse {
  0%, 100% { background: #fff; }
  50% { background: #f0f0f0; }
}

/* 好:僅動畫層重繪 */
.container {
  position: relative;
}
.container::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0);
  animation: pulse 1s infinite;
  will-change: opacity;
}
@keyframes pulse {
  0%, 100% { opacity: 0; }
  50% { opacity: 0.05; }
}

避免層爆炸

/* 差:每個列表項都建立合成層 */
.list-item {
  will-change: transform;
}

/* 好:僅動畫中的元素提升 */
.list-item.is-animating {
  will-change: transform;
}

contain 屬性隔離

.card {
  contain: layout style paint;
}

contain 告訴瀏覽器該元素的樣式/版面配置/繪製不會影響外部,瀏覽器可以安全最佳化。


效能測量

Chrome DevTools Layers 面板

  1. 開啟 DevTools → More tools → Layers
  2. 檢視合成層數量和顯示記憶體佔用
  3. 識別不必要的層提升

Performance 面板分析

幀時間線中:
- 綠色 = 繪製(Paint)
- 紫色 = 版面配置(Layout)
- 透明 = 合成(Composite)← 理想狀態

幀率偵測

let lastTime = performance.now();
let frames = 0;

function measureFPS() {
  frames++;
  const now = performance.now();
  if (now - lastTime >= 1000) {
    console.log(`FPS: ${frames}`);
    frames = 0;
    lastTime = now;
  }
  requestAnimationFrame(measureFPS);
}
requestAnimationFrame(measureFPS);

最佳化檢查清單

  • 動畫僅使用 transformopacity
  • 避免在動畫中修改 width/height/top/left
  • will-change 僅在動畫前設定、動畫後移除
  • 合成層數量 < 30
  • 使用 contain: layout style paint 隔離重繪範圍
  • 複雜動畫使用 Web Animations API 取得精細控制
  • 捲動驅動動畫使用 requestAnimationFrame 節流

總結

高效能動畫的核心是讓 GPU 做 GPU 擅長的事:只動畫 transformopacity,透過 will-change 預告瀏覽器,控制合成層數量避免顯示記憶體浪費。Web Animations API 提供了與 CSS 動畫相同的效能底座,同時帶來了 JS 層級的精細控制能力。

使用 CSS動畫工具 快速預覽動畫效果,使用 漸層產生器 建立高效能背景動畫,使用 圖片變換 處理圖片的 transform 操作。

本站提供瀏覽器本地工具,免註冊即可試用 →

#Web Animations API#动画性能#requestAnimationFrame#GPU加速#composite