嗨咿,我是 illumi,昨天講到用 AE 輸出透明影片轉 WebM 放到網頁上,結果發現 Safari 完全不支援 WebM,甚至在 iPad / iPhone 的 Chrome 裡也會壞掉!
這下是不是整個互動動畫要全爆?別急,第二條路線:PNG / WebP Sequence。
WebM 在 Chrome / Firefox / Edge 超香,檔案比 MP4 小一半以上,但 Safari → 直接擋。
透明影片最慘,會整個背景黑掉,動畫就毀了。
可以用「逐格貼圖」的土炮解法,也就是 PNG Sequence:
在 AE 裡:
Composition > Add to Media Encoder

畫面顯示:
這樣輸出就會變成逐格的 PNG 檔案序列。
在 AE 上方選單 → Composition > Add to Render Queue
AE 下方會出現 Render Queue 面板
在 Output Module → 選 Format: PNG Sequence,再選 RGB + Alpha
Composition > Add to Render Queue
在 Render Queue → Output Module 選單 → 改成 PNG Sequence
(記得 Channels 要選 RGB + Alpha 才有透明背景!)
選擇輸出資料夾,按下渲染 → 就會得到一包圖片
PNG 有透明很好,但檔案通常超肥。這時候可以在終端機中,用 cwebp 批次轉檔:
cwebp(Google 官方 WebP 工具)
brew install webp
q 80 = 品質 80(0~100),可依需求調整。mkdir webp_output
for f in *.png; do
  cwebp -q 80 "$f" -o "webp_output/${f%.png}.webp";
done
來了!我們把這堆 Sequence 放到 public/sequence/資料夾名稱/,
每個圖檔名稱叫frame01......(可自行更改)
再用一個小小的 React 元件讀進來,Canvas 播放就搞定:
import React, { useEffect, useRef, useState } from "react";
interface SequencePlayerProps {
  folder: string; // e.g. "monsterCurious69"
  frameCount: number; // 幀數,例如 60
  width?: number;
  height?: number;
  fps?: number; // 每秒幀數
}
const SequencePlayer: React.FC<SequencePlayerProps> = ({
  folder,
  frameCount,
  width = 600,
  height = 600,
  fps = 24,
}) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [images, setImages] = useState<HTMLImageElement[]>([]);
  const [loaded, setLoaded] = useState(0);
  useEffect(() => {
    const imgs: HTMLImageElement[] = [];
    let loadedCount = 0;
    for (let i = 0; i < frameCount; i++) {
      const img = new Image();
      img.src = `/sequence/${folder}/frame${i
        .toString()
        .padStart(2, "0")}.webp`;  // 這一行最重要 要放資料夾路徑和圖檔名都叫什麼,2表示圖檔後面的數字都有幾位數
      img.onload = () => {
        loadedCount++;
        setLoaded(loadedCount); // 更新進度
        if (loadedCount === frameCount) {
          setImages(imgs);
        }
      };
      imgs.push(img);
    }
  }, [folder, frameCount]);
  // 播放動畫
  useEffect(() => {
    if (images.length === 0) return;
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    let frame = 0;
    const interval = 1000 / fps;
    let lastTime = performance.now();
    const render = (time: number) => {
      if (time - lastTime >= interval) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(images[frame], 0, 0, canvas.width, canvas.height);
        frame = (frame + 1) % images.length;
        lastTime = time;
      }
      requestAnimationFrame(render);
    };
    requestAnimationFrame(render);
  }, [images, fps]);
  return (
    <div className="flex justify-center items-center">
      <canvas
        ref={canvasRef}
        width={width}
        height={height}
        className=" bg-transparent"
      />
      {loaded < frameCount && (
        <p className="absolute text-sm text-gray-500">
          Loading... {loaded}/{frameCount}
        </p>
      )}
    </div>
  );
};
export default SequencePlayer;
呼叫的時候很簡單:
<SequencePlayer
  folder="資料夾名稱"
  frameCount={60}
  width={100}
  height={100}
  fps={24}
 />

(這是在Safari打開的:綠色背景為Webm,透明的為WebP Sequence)