spring() — 彈簧物理動畫
spring() 是 Remotion 的物理引擎動畫原語,使用彈簧模擬讓動畫更自然、更有生命力。
spring()
spring() 是一個基於物理的動畫原語,透過模擬彈簧的物理特性讓你的動畫看起來更自然流暢。
基本用法
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const value = spring({
frame,
fps,
config: {
stiffness: 100,
},
});在以下範例中,value 用於控制 div 的 scale 屬性:
import {useCurrentFrame, useVideoConfig, spring, AbsoluteFill} from 'remotion';
export const SpringDemo: React.FC = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const scale = spring({
frame,
fps,
config: {
damping: 10,
mass: 1,
stiffness: 100,
},
});
return (
<AbsoluteFill
style={{
backgroundColor: '#0b84f3',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<div
style={{
width: 100,
height: 100,
backgroundColor: 'white',
borderRadius: 10,
transform: `scale(${scale})`,
}}
/>
</AbsoluteFill>
);
};若要關閉預設的彈跳效果,請增加 damping 參數的值(例如設為 100)。
如需更進階的互動式展示,請訪問 remotion.dev/timing-editor。
參數
frame
目前的時間值。大多數時候你會傳入 useCurrentFrame() 的回傳值。
彈簧動畫從第 0 幀開始,所以如果你想要延遲動畫,可以傳入不同的值,例如 frame - 20。
const frame = useCurrentFrame();
// 動畫從第 20 幀開始
const value = spring({
frame: frame - 20,
fps,
});fps
每秒幀數,用於計算彈簧動畫。這應該始終是 useVideoConfig() 回傳值的 fps 屬性。
const {fps} = useVideoConfig();from?
預設值:0
動畫的初始值。
to?
預設值:1
動畫的最終值。
請注意,根據參數設定,彈簧動畫可能會稍微超過目標值,然後再彈回最終目標。
reverse? v3.3.92
預設值:false
反向播放動畫。請參閱:操作順序。
const value = spring({
frame,
fps,
reverse: true, // 動畫從 1 到 0
});config?
一個可選的物件,允許你自訂動畫的物理特性。
mass?
預設值:1
彈簧的重量。減少質量會讓動畫變快!
damping?
預設值:10
動畫的減速程度。值越高,彈跳越少;設為 100 可以完全消除彈跳。
stiffness?
預設值:100
彈簧剛性係數。調整這個值會影響動畫的彈跳感。值越高,動畫越快速有力。
overshootClamping?
預設值:false
決定動畫是否可以超越 to 值。
如果設為 true,當動畫超過 to 時,它會直接回傳 to 的值,而不會有任何超衝(overshoot)。
durationInFrames? v3.0.27
將動畫曲線拉伸到你指定的確切長度。
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const value = spring({
frame,
fps,
config: {
stiffness: 100,
},
durationInFrames: 40, // 動畫精確持續 40 幀
});另請參閱:操作順序
durationRestThreshold? v3.0.27
動畫應該接近結尾多少才算「完成」,用於計算持續時間。僅在也指定了 durationInFrames 時才有效。
例如,如果給定 durationRestThreshold 為 0.001,且 durationInFrames 為 30,這意味著在 30 幀後,彈簧已經達到了距離最終值 99.9%(1 - 0.001 = 0.999)的位置。
delay? v3.3.90
延遲動畫的幀數。
例如,如果給定 delay 為 25,則第 0-24 幀會回傳初始值,動畫實際上從第 25 幀開始。
另請參閱:操作順序
const value = spring({
frame,
fps,
delay: 25, // 等待 25 幀後才開始動畫
});操作順序
以下是 durationInFrames、reverse 和 delay 這些操作的應用順序:
- 首先,如果你傳入了
durationInFrames,彈簧動畫會被拉伸到你指定的持續時間 - 然後,如果你傳入了
reverse: true,動畫會被反向播放 - 最後,如果你傳入了
delay,動畫會被延遲
實用範例
結合 interpolate() 使用
spring() 回傳 0 到 1 之間的進度值,你可以用 interpolate() 將其映射到任何範圍:
import {useCurrentFrame, useVideoConfig, spring, interpolate, AbsoluteFill} from 'remotion';
export const SlideAndFade: React.FC = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const progress = spring({
frame,
fps,
config: {damping: 12},
});
const translateY = interpolate(progress, [0, 1], [60, 0]);
const opacity = interpolate(progress, [0, 0.4], [0, 1], {
extrapolateRight: 'clamp',
});
return (
<AbsoluteFill
style={{
backgroundColor: '#1a1a2e',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<h1
style={{
color: 'white',
fontSize: 72,
fontWeight: 'bold',
transform: `translateY(${translateY}px)`,
opacity,
}}
>
你好,Remotion!
</h1>
</AbsoluteFill>
);
};使用 durationInFrames 精確控制時間
import {useCurrentFrame, useVideoConfig, spring} from 'remotion';
export const TimedSpring: React.FC = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
// 動畫精確持續 1 秒(假設 30fps 則為 30 幀)
const scale = spring({
frame,
fps,
config: {stiffness: 200, damping: 15},
durationInFrames: 30,
});
return (
<div
style={{
transform: `scale(${scale})`,
width: 150,
height: 150,
backgroundColor: '#e74c3c',
borderRadius: '50%',
}}
/>
);
};延遲序列動畫
import {useCurrentFrame, useVideoConfig, spring, AbsoluteFill} from 'remotion';
const items = ['第一項', '第二項', '第三項', '第四項'];
export const StaggeredList: React.FC = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
return (
<AbsoluteFill
style={{
backgroundColor: '#2c3e50',
padding: 60,
display: 'flex',
flexDirection: 'column',
gap: 20,
justifyContent: 'center',
}}
>
{items.map((item, index) => {
const opacity = spring({
frame,
fps,
delay: index * 8, // 每個項目延遲 8 幀
config: {damping: 15},
});
const translateX = spring({
frame,
fps,
delay: index * 8,
from: -50,
to: 0,
config: {damping: 15},
});
return (
<div
key={item}
style={{
opacity,
transform: `translateX(${translateX}px)`,
backgroundColor: 'rgba(255,255,255,0.1)',
padding: '20px 30px',
borderRadius: 8,
color: 'white',
fontSize: 32,
}}
>
{item}
</div>
);
})}
</AbsoluteFill>
);
};反向動畫(淡出效果)
import {useCurrentFrame, useVideoConfig, spring} from 'remotion';
export const FadeOut: React.FC = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
// reverse: true 讓動畫從 1 到 0(淡出)
const opacity = spring({
frame,
fps,
reverse: true,
config: {damping: 20},
durationInFrames: 40,
});
return (
<div style={{opacity, fontSize: 60, color: 'white'}}>
淡出效果
</div>
);
};參數速查表
| 參數 | 型別 | 預設值 | 說明 |
|---|---|---|---|
frame | number | 必填 | 目前幀數 |
fps | number | 必填 | 每秒幀數 |
from | number | 0 | 動畫起始值 |
to | number | 1 | 動畫目標值 |
delay | number | 0 | 延遲幀數 |
reverse | boolean | false | 是否反向播放 |
durationInFrames | number | 自動 | 固定動畫持續幀數 |
durationRestThreshold | number | 0.005 | 完成判定閾值 |
config.mass | number | 1 | 質量(越大越慢) |
config.damping | number | 10 | 阻尼(越大彈跳越少) |
config.stiffness | number | 100 | 剛性(越大越快速) |
config.overshootClamping | boolean | false | 是否限制超衝 |
相容性
| 環境 | 支援 |
|---|---|
| Chrome | ✓ |
| Firefox | ✓ |
| Safari | ✓ |
| Node.js | ✓ |
| Bun | ✓ |
| Serverless 函式 | ✓ |
| 客戶端渲染 | ✓ |
| 伺服器端渲染 | ✓ |
| Player | ✓ |
| Studio | ✓ |
致謝
此函式取自 Reanimated 2,它本身也是 Remotion 的重要靈感來源。
相關資源
- 動畫入門 — 了解
useCurrentFrame、interpolate和spring的基礎 - interpolate() — 搭配 spring() 使用的值映射函式
- measureSpring() — 計算彈簧動畫的持續時間
- timing-editor — 互動式彈簧參數調整工具
- 彈簧動畫範例 — 官方互動式範例