跳至主要內容
Skip to content

精準控制動畫流:深入 Web Animations API (WAAPI)

在網頁開發中,我們習慣使用 CSS Transitions 或 Animations 來實現視覺效果。然而,當動畫變得複雜(例如:需要動態計算路徑、精確的播放控制、或是在動畫結束後執行複雜邏輯)時,CSS 就會顯得力不從心。

這就是 Web Animations API (WAAPI) 大顯身手的地方。它結合了 CSS 的效能與 JavaScript 的靈活性,讓我們能用程式碼完整掌控動畫的生命週期。


一、 為什麼選擇 Web Animations API?

  1. 程式化控制:支援 play()pause()reverse()finish() 等方法。
  2. 效能最佳化:與 CSS 動畫一樣,運行在瀏覽器的合成器執行緒(Compositor Thread),避免阻塞主執行緒。
  3. 動態屬性:關鍵影格(Keyframes)可以用 JavaScript 動態產生,不需預先寫死在 CSS 中。
  4. Promise 支援:原生的 finished 屬性是一個 Promise,方便串接多個動畫或回呼函數。

二、 WAAPI 核心基礎

2.1 基本語法

javascript
const animation = element.animate(
  // 1. 關鍵影格 (Keyframes)
  [
    { transform: 'translateX(0)', opacity: 0 },
    { transform: 'translateX(100px)', opacity: 1 }
  ],
  // 2. 動畫選項 (Timing Options)
  {
    duration: 1000,
    iterations: Infinity,
    easing: 'ease-in-out'
  }
);

2.2 控制方法

javascript
animation.pause();   // 暫停
animation.play();    // 播放
animation.reverse(); // 倒放
animation.cancel();  // 取消並重置

三、 封裝 useAnimate Composable

為了讓動畫在 Vue 元件中更易於管理,我們實作一個 useAnimate

3.1 完整實作

typescript
// composables/useAnimate.ts
import { ref, onUnmounted, type Ref } from 'vue';

export interface AnimateOptions extends KeyframeAnimationOptions {
  immediate?: boolean;
}

export function useAnimate(
  target: Ref<HTMLElement | null>,
  keyframes: Keyframe[] | PropertyIndexedKeyframes,
  options: AnimateOptions = {}
) {
  const { immediate = true, ...animationOptions } = options;
  const animation = ref<Animation | null>(null);
  const playState = ref<AnimationPlayState>('idle');

  const play = () => {
    if (!target.value) return;
    
    // 如果動畫不存在或已被取消,則建立新動畫
    if (!animation.value || animation.value.playState === 'finished') {
      animation.value = target.value.animate(keyframes, animationOptions);
      
      // 監聽狀態變化
      animation.value.onfinish = () => (playState.value = 'finished');
      animation.value.oncancel = () => (playState.value = 'idle');
    } else {
      animation.value.play();
    }
    playState.value = 'running';
  };

  const pause = () => {
    animation.value?.pause();
    playState.value = 'paused';
  };

  const reverse = () => {
    animation.value?.reverse();
    playState.value = 'running';
  };

  const cancel = () => {
    animation.value?.cancel();
    playState.value = 'idle';
  };

  // 如果設定了 immediate,在組件掛載或 target 準備好時播放
  if (immediate) {
    // 這裡可以使用 watch 或 onMounted 確保 target 已掛載
  }

  onUnmounted(() => cancel());

  return {
    animation,
    playState,
    play,
    pause,
    reverse,
    cancel,
    finished: animation.value?.finished
  };
}

四、 實戰範例:進場動畫與手動控制

vue
<script setup lang="ts">
import { ref } from 'vue';
import { useAnimate } from '@/composables/useAnimate';

const boxRef = ref<HTMLElement | null>(null);

const { play, pause, reverse, playState } = useAnimate(
  boxRef,
  [
    { transform: 'scale(0.5) rotate(0deg)', borderRadius: '50%' },
    { transform: 'scale(1.2) rotate(180deg)', borderRadius: '10%' },
    { transform: 'scale(1) rotate(360deg)', borderRadius: '20%' }
  ],
  {
    duration: 2000,
    fill: 'forwards',
    easing: 'cubic-bezier(0.4, 0, 0.2, 1)'
  }
);
</script>

<template>
  <div class="demo-container">
    <div ref="boxRef" class="animated-box">Vue + WAAPI</div>
    
    <div class="controls">
      <p>當前狀態:{{ playState }}</p>
      <button @click="play">播放 / 繼續</button>
      <button @click="pause">暫停</button>
      <button @click="reverse">倒放</button>
    </div>
  </div>
</template>

<style scoped>
.animated-box {
  width: 150px;
  height: 150px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 50px auto;
  font-weight: bold;
  box-shadow: 0 10px 20px rgba(0,0,0,0.2);
}
.controls {
  text-align: center;
  gap: 10px;
}
</style>

五、 技術深度解析

5.1 動態 Keyframes 的威力

不同於 CSS,你可以根據使用者的滑鼠位置或目前的資料狀態,動態生成下一組動畫:

javascript
const dynamicKeyframes = [
  { left: `${currentX}px` },
  { left: `${targetX}px` }
];
element.animate(dynamicKeyframes, 500);

5.2 瀏覽器支援狀況

WAAPI 已在主流瀏覽器中獲得良好支援:

特性ChromeEdgeFirefoxSafari
基礎核心支援支援支援支援
finished Promise支援支援支援支援
composite 操作支援支援支援部分支援

總結

Web Animations API 填補了 CSS 與 JavaScript 動畫之間的鴻溝。透過 useAnimate Composable,我們能夠在 Vue 的宣告式世界中,精確地、程序化地控制每一個關鍵影格,創造出更為流暢且具備互動性的使用者體驗。


延伸閱讀