嗨咿,我是 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)