mcpField
R
remotion-keyframes

remotion-keyframes

github.com/phamthanhnghiaクリエイティブ・メディア362 回インストール14 週間前に更新
リポジトリ

remotion-keyframes について

remotion-keyframesは、Remotionでフレームごとに再現可能なアニメーションを作り、Studioで状態を確認するスキルです。被写体の連続性や見えるポーズを重視し、最終フレームまで検証します。

主な機能

  1. 01現在のフレームからアニメーション値を算出
  2. 02springとinterpolateによるキーフレーム作成
  3. 03フレーム遅延による複数要素の順次表示
  4. 04Studioでタイムライン全体を確認
  5. 05描画上重要な処理で非決定的な動きを回避

活用シーン

  1. 01登場、遷移、複数段階のアニメーション制作
  2. 02単語ごとのテキスト表示やSVG線描画の実装
  3. 03最初のフレーム、各ポーズ、最終フレームの確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/phamthanhnghia/remotion-agent-skills --skill remotion-keyframes

インストール後、Claude Code で/remotion-keyframesを入力して読み込まれたか確認します。

詳細

Remotion キーフレーム

キーフレームは、目に見える状態、連続した被写体の同一性、フレームごとの決定性、検証済みのピクセルを保証する契約です。

幅広いシーンのレシピには remotion-animation を使用してください。コマンドの全ドキュメントには remotion-cli を使用してください。

手順

  1. アニメーションする被写体、目に見える状態、最終状態、アニメーションのプリミティブ(spring または interpolate)を特定します。
  2. プロンプトの要件を証明できる、最小限の仕組みを選びます。
  3. フレームごとに決定されるキーフレームを作成します。すべての値は useCurrentFrame() から導出します。
  4. npx remotion studio で検証します。タイムライン全体をスクラブし、最初のフレーム、すべてのポーズ、最終フレームを確認します。
  5. 検証に失敗した場合は、ソースのキーフレームを修正して再検証してから、レンダリングします。

契約

  • 動く被写体を明示します。
  • 意図した動きを証明するために必要なポーズを、最終状態も含めて明示します。
  • 隠れた補助状態ではなく、目に見えるチャンネルをキーフレーム化します。
  • 連続性が重要な場合は、オブジェクトの同一性を維持します。
  • 読み取り可能な状態や意味のある状態は、認識できるだけの時間保持します(テキストは最低10フレーム、アイコンは最低6フレーム)。
  • 最終フレームもアニメーションの一部であり、後処理ではありません。
  • 指示がない限り、静止状態に戻さないでください。
  • 指示がない限り、黒画面で終わらせないでください。

ランタイムルール

interpolate() と spring():

  • すべての値は frame から同期的に計算します。interpolate/spring の呼び出し内で非同期処理を行わないでください。
  • 値が意図した範囲外に漏れないよう、extrapolateLeft: 'clamp' と extrapolateRight: 'clamp' を指定します。
  • spring({frame, fps, config}) — frame には、導出した Date.now() の値ではなく、Remotion の useCurrentFrame() の値を指定します。
  • 登場のアニメーションでは、スプリングにオフセットを設定します:spring({frame: frame - delay, fps, config})。負のフレーム値でスプリングが動作しないよう、Math.max(0, frame - delay) を使用します。

レンダリングに重要な動きには、次を使用しないでください。

  • Date.now() または performance.now()
  • シードを指定しない Math.random()(代わりに Remotion の random(seed) を使用します)
  • CSS トランジションまたは CSS アニメーション(シークできません)
  • requestAnimationFrame
  • レンダリング処理内の setTimeout/setInterval
  • 視覚プロパティの値に対する useState/useEffect

スプリング設定のリファレンス

import {spring, useCurrentFrame, useVideoConfig} from 'remotion';

const frame = useCurrentFrame();
const {fps} = useVideoConfig();

// Snappy entrance (default feel)
const scale = spring({frame, fps, config: {damping: 200}});

// Bouncy entrance
const bounce = spring({frame, fps, config: {damping: 8, stiffness: 300}});

// Very smooth, slow
const smooth = spring({frame, fps, config: {damping: 30, stiffness: 80, mass: 2}});

// Sharp exit (reverse spring — animate from 1 to 0)
const {durationInFrames} = useVideoConfig();
const exitFrame = durationInFrames - frame;
const exitScale = spring({frame: exitFrame, fps, config: {damping: 200}});
const scaleValue = 1 - (1 - exitScale) ; // goes from 1 down to 0

補間レシピ

import {interpolate} from 'remotion';

// Fade in over 20 frames
const opacity = interpolate(frame, [0, 20], [0, 1], {extrapolateRight: 'clamp'});

// Slide in from left
const x = interpolate(frame, [0, 30], [-300, 0], {
  extrapolateLeft: 'clamp',
  extrapolateRight: 'clamp',
});

// Multi-stop color shift
const r = interpolate(frame, [0, 30, 60], [255, 100, 0], {extrapolateRight: 'clamp'});
const g = interpolate(frame, [0, 30, 60], [0, 150, 255], {extrapolateRight: 'clamp'});

// Scale pop
const scale = interpolate(frame, [0, 8, 12, 18], [0, 1.15, 0.95, 1], {
  extrapolateLeft: 'clamp',
  extrapolateRight: 'clamp',
});

チャンネル

コンポジター/視覚チャンネルを優先します:transform: translateX/Y/Z、transform: scale/rotate/skew、opacity、borderRadius、CSS 変数、clipPath、SVG の strokeDashoffset。

レイアウトチャンネルは避けてください:top/left/right/bottom、width/height、margin/padding。これらはリフローを引き起こして遅く、レンダラーがフレームを並列にサンプリングする際に不正確な結果を生みます。

スタッガーパターン

const ITEM_COUNT = 5;
const STAGGER_FRAMES = 8; // delay between each item

const items = Array.from({length: ITEM_COUNT}, (_, i) => {
  const delayedFrame = Math.max(0, frame - i * STAGGER_FRAMES);
  const itemSpring = spring({frame: delayedFrame, fps, config: {damping: 200}});
  return {opacity: itemSpring, y: (1 - itemSpring) * 40};
});

テキストアニメーションのパターン

const words = 'Hello Remotion World'.split(' ');

const WordReveal = () => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  return (
    <div style={{display: 'flex', gap: 12}}>
      {words.map((word, i) => {
        const delayedFrame = Math.max(0, frame - i * 6);
        const progress = spring({frame: delayedFrame, fps, config: {damping: 200}});
        return (
          <span
            key={i}
            style={{
              opacity: progress,
              transform: `translateY(${(1 - progress) * 20}px)`,
              display: 'inline-block',
            }}
          >
            {word}
          </span>
        );
      })}
    </div>
  );
};

SVG のストローク描画

// SVG stroke-dasharray draw-on effect
const pathLength = 500; // measure with path.getTotalLength() in development
const drawProgress = interpolate(frame, [0, 60], [0, 1], {extrapolateRight: 'clamp'});

<path
  d="M 0 0 L 300 200"
  stroke="white"
  strokeWidth={4}
  fill="none"
  strokeDasharray={pathLength}
  strokeDashoffset={pathLength * (1 - drawProgress)}
/>

3D の奥行き(CSS パースペクティブ)

// CSS perspective 3D scene
<div style={{perspective: 800, perspectiveOrigin: '50% 50%'}}>
  <div
    style={{
      transformStyle: 'preserve-3d',
      transform: `rotateY(${interpolate(frame, [0, 60], [45, 0], {extrapolateRight: 'clamp'})}deg)`,
    }}
  >
    {/* 3D content */}
  </div>
</div>

仕組みの選択

必要なもの仕組み
自然に減速する登場spring()
時間指定の値の遷移interpolate()
複数の停止点を含むシーケンス複数の入力/出力停止点を指定した interpolate()
順番に登場する多数のアイテムスタッガー:spring() 内でインデックスに応じて frame - i * delay を指定
テキストの表示(単語ごと)単語のインデックスごとにスプリングをスタッガー
ストロークが伸びる、または線を描くinterpolate() を使った strokeDashoffset
形状の表示境界interpolate() を使った clipPath
3D の奥行きがあるシーンinterpolate() を使った CSS の perspective + rotateX/Y/Z

エラー処理

問題修正方法
特定のフレームで値が飛ぶspring() に負の frame が渡されていないか確認します。Math.max(0, frame - delay) でクランプします。
アニメーションが正しくシークできない値を駆動している useState/CSS アニメーションがないことを確認します。
開始時または終了時に黒いフレームが表示される登場時に extrapolateLeft: 'clamp'、終了時に extrapolateRight: 'clamp' が設定されているか確認します。
テキストが点滅する/一貫しないrandom() のシードが Math.random() ではなく、安定した文字列であることを確認します。
スプリングが画面外まで行き過ぎるdamping を大きくします(200 = 行き過ぎなし、8 = 大きく弾みます)。

完了

Studio で検証します。最初のフレーム、目に見えるすべてのポーズ、最終フレームをスクラブして確認します。被写体に紐づいた動きであること、デバッグ用オーバーレイがないこと、最初のフレームでフラッシュがないことを確認します。

コメント