動畫入門
學會 useCurrentFrame、interpolate 和 spring 三大動畫技巧,打造流暢的 Remotion 動畫。
為什麼不能用 CSS Animation?
你可能會想:「我直接用 CSS animation 或 transition 不就好了?」
答案是不行。原因在於 Remotion 的渲染方式——它是逐幀渲染的。Remotion 不是在瀏覽器裡播放動畫,而是一幀一幀地截圖,再組合成影片。CSS 動畫依賴瀏覽器的時間系統,但 Remotion 需要的是:「在第 N 幀,這個元素應該長什麼樣?」
所以,所有動畫都必須是 frame 的函數。
基礎:useCurrentFrame()
useCurrentFrame() 是一切的起點。它回傳目前的幀數,你用這個數字來計算任何視覺屬性:
import { useCurrentFrame } from "remotion";
export const FadeIn: React.FC = () => {
const frame = useCurrentFrame();
const opacity = Math.min(1, frame / 30);
return (
<div style={{ opacity, fontSize: 60, color: "white" }}>
淡入效果
</div>
);
};前 30 幀內,opacity 從 0 線性增加到 1。
進階:interpolate()
手動算數學很快就會變得複雜。interpolate() 幫你做值的映射:
import { useCurrentFrame, interpolate } from "remotion";
export const SlideIn: React.FC = () => {
const frame = useCurrentFrame();
const translateX = interpolate(frame, [0, 30], [200, 0], {
extrapolateRight: "clamp",
});
const opacity = interpolate(frame, [0, 20], [0, 1], {
extrapolateRight: "clamp",
});
return (
<div
style={{
transform: `translateX(${translateX}px)`,
opacity,
fontSize: 48,
color: "white",
}}
>
從右側滑入
</div>
);
};interpolate(frame, [0, 30], [200, 0]) 的意思是:「當 frame 從 0 到 30 時,把值從 200 映射到 0。」extrapolateRight: "clamp" 確保超過 30 幀後值不會繼續變化。
彈跳效果:spring()
interpolate() 產生的是線性動畫,看起來有點機械。spring() 模擬彈簧物理,讓動畫更自然:
import { useCurrentFrame, useVideoConfig, spring } from "remotion";
export const BounceIn: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scale = spring({
frame,
fps,
config: { damping: 8, stiffness: 200 },
});
return (
<div
style={{
transform: `scale(${scale})`,
fontSize: 60,
color: "white",
}}
>
彈跳!
</div>
);
};spring 的參數
| 參數 | 預設值 | 效果 |
|---|---|---|
damping | 10 | 越高 = 越少彈跳 |
stiffness | 100 | 越高 = 動畫越快 |
mass | 1 | 越高 = 越慢、越有慣性 |
實戰:組合多種效果
把以上技巧結合,做一個帶有滑動 + 彈跳 + 淡入的標題動畫:
import {
useCurrentFrame,
useVideoConfig,
spring,
interpolate,
} from "remotion";
export const AnimatedTitle: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const progress = spring({
frame,
fps,
config: { damping: 12 },
});
const translateY = interpolate(progress, [0, 1], [50, 0]);
const opacity = interpolate(progress, [0, 0.5], [0, 1], {
extrapolateRight: "clamp",
});
return (
<div
style={{
transform: `translateY(${translateY}px)`,
opacity,
fontSize: 72,
color: "white",
fontWeight: "bold",
}}
>
組合動畫效果
</div>
);
};這裡的技巧是:先用 spring() 產生一個 0→1 的進度值,再用 interpolate() 把這個進度值映射到不同的視覺屬性。這樣所有屬性會跟著同一個彈簧曲線同步變化。
下一步
學會了基本動畫技巧後,接下來可以學習如何用 Sequence 組織影片結構,打造更複雜的影片專案。