Remotion LabRemotion Lab
›核心概念›spring() — 彈簧物理動畫

spring() — 彈簧物理動畫

spring() 是 Remotion 的物理引擎動畫原語,使用彈簧模擬讓動畫更自然、更有生命力。

spring()

spring() 是一個基於物理的動畫原語,透過模擬彈簧的物理特性讓你的動畫看起來更自然流暢。

基本用法

spring-example.ts
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

將動畫曲線拉伸到你指定的確切長度。

spring-example.ts
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 這些操作的應用順序:

  1. 首先,如果你傳入了 durationInFrames,彈簧動畫會被拉伸到你指定的持續時間
  2. 然後,如果你傳入了 reverse: true,動畫會被反向播放
  3. 最後,如果你傳入了 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>
  );
};

參數速查表

參數型別預設值說明
framenumber必填目前幀數
fpsnumber必填每秒幀數
fromnumber0動畫起始值
tonumber1動畫目標值
delaynumber0延遲幀數
reversebooleanfalse是否反向播放
durationInFramesnumber自動固定動畫持續幀數
durationRestThresholdnumber0.005完成判定閾值
config.massnumber1質量(越大越慢)
config.dampingnumber10阻尼(越大彈跳越少)
config.stiffnessnumber100剛性(越大越快速)
config.overshootClampingbooleanfalse是否限制超衝

相容性

環境支援
Chrome✓
Firefox✓
Safari✓
Node.js✓
Bun✓
Serverless 函式✓
客戶端渲染✓
伺服器端渲染✓
Player✓
Studio✓

致謝

此函式取自 Reanimated 2,它本身也是 Remotion 的重要靈感來源。

相關資源