Communitygithub.com

iamhuynq/video-remotion

インタラクティブな編集を可能にするRemotionマークアップの構造化

video-remotion とは?

このスキルは、Remotion Studioでインタラクティブ編集を可能にするための、Remotionマークアップの構造化ルールを提供します。具体的には、要素のクリック選択、ドラッグ&ドロップ、リサイズ、回転、CSSスタイルの編集、キーフレームやイージングの編集を実現します。 主な特徴として、`Track`や`Interactive.create()`を用いた対話的コンポーネントの作成、スキーマフィールドによるプロパティの編集可能性の制御(文字列、数値、色、列挙型、アセットなど)、各要素に個別のJSXノードを割り当てることで独立した編集を可能にする点が挙げられます。また、スタイルはインラインで記述し、アニメーションは`interpolate()`を直接マークアップ内で使用することで、Studioが標準化・キーフレーム化できるようにします。 このスキールはClaude Code、Cursor、CodexなどのAIエージェントで利用でき、開発者がRemotion Studioの対話性を最大限に活用したビデオコンポーネントを効率的に作成するのに役立ちます。複雑なマークアップはグレーアウトされるため、シンプルで構造化されたコードを書くことが推奨されます。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/iamhuynq/video-remotion/tree/HEAD/.agents/skills/remotion-interactivity

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

プレビュー

スキル README より

video-remotion preview

ドキュメント

Remotionマークアップを特定の方法で記述することで、Remotion Studioがコードの構造を認識し、対話的に操作できるようになります。

  • 項目をクリックして選択可能
  • ドラッグ&ドロップ、リサイズ、回転が可能
  • CSSスタイルの編集
  • キーフレームとイージング値の編集が可能

マークアップが複雑すぎてStudioが対話的に操作できない場合、値はグレーアウト表示されます。

独自のタイムラインを持つ対話的コンポーネントを優先する

Track、接続されたコンポジション、およびInteractive.create()を使用して新しいコンポジションを構築します。

シーンにはlayoutを明示的に'absolute-fill'に設定し、境界要素には'none'を設定します。エクスポートされたシーンと再利用可能な要素コンポーネントをCompositionで登録します。

最初に完全なTrack + 接続コンポジションの例から始めてください。 この例には、要素、シーン、親タイムライン、ルート登録が含まれています。

import {Track, useVideoConfig} from 'remotion';
import {Chapter} from './Chapter';

export const MyVideo = () => {
  const {fps} = useVideoConfig();

  return (
    <Track name="Chapters">
      <Chapter
        name="Opening"
        durationInFrames={3 * fps}
        title="Introduction"
      />
      <Chapter
        name="Feature"
        from={3 * fps}
        durationInFrames={4 * fps}
        title="New features"
      />
    </Track>
  );
};

各シーンインスタンスに背景と配置スタイルを設定します(完全な例を参照)。レイアウトのみのコンテナはプレーンなHTMLとして扱います。

スキーマフィールドの選択

スキーマの各キーはプロパティであり、Studioが呼び出し箇所で読み取り、編集できます。スキーマにないプロパティも機能しますが、Studioでは編集できません。キーはstyle.colorのようなドット表記を使用できます。

タイプ使用目的キーフレーム可能
stringタイトル、プロンプト、コードスニペットなどの文字列いいえ
number数値。オプションでmin、max、step、integerはい
booleanオン/オフスイッチはい(ホールド)
colorCSSカラー文字列はい
enumvariants間の選択。それぞれにネストされたスキーマオプトイン(ホールド)
array数値、色、列挙型などのリストいいえ
assetメディアソース。オプションでassetTypeいいえ
font-familyCSSフォントファミリーいいえ
font-weightフォントの太さはい
translate, scale, rotation-css, rotation-degrees, transform-originトランスフォームはい
uv-coordinate要素上の正規化された[x, y]ポイントはい
svg-pathSVGパスデータはい
remotion-captionsCaption[]データいいえ
hiddenコントロールから除外される値—

すべてのフィールドにはdefaultが必要で、Studioがラベルとして表示するdescriptionを持つべきです。 keyframable: falseを設定すると、静的なコントロールが表示されます。

テキストやアクセントカラーなど、コンテンツとインスタンスごとの外観を公開します。 style.translate、style.scale、style.rotate、style.transformOrigin、style.opacityのフィールドは追加しないでください。これらはInteractive.create()が自動的に追加します。

ルートのCSSプロパティをインスタンスごとに編集可能にするには、組み込みのスキーマフラグメントを展開します。

const cardSchema = {
  ...Interactive.textSchema, // style.color, style.fontSize, style.fontWeight, ...
  ...Interactive.backgroundSchema, // style.backgroundColor
  ...Interactive.borderRadiusSchema, // style.borderRadius, ...
} as const satisfies InteractivitySchema;

また、Interactive.borderSchemaと、キャプションを受け入れるコンポーネント用のInteractive.captionsSchemaも利用できます。 React.ReactNodeの子要素を受け入れるコンポーネントの場合は、...Interactive.childrenSchemaを追加します。 これは子コンテンツのコントロールを提供し、将来的にリッチなコンテンツをサポートするために進化する可能性があります。 現在は、デフォルトが''の「Text」というラベルの文字列フィールドを使用します。 コンポーネントが文字列のみの子要素を必要とする場合は、代わりにtype: 'string'の明示的なchildrenフィールドを宣言します。 Interactive.textSchemaはタイポグラフィスタイルを制御し、childrenSchemaはコンテンツを制御します。 子要素フラグメントはbaseSchemaに含まれておらず、カスタムコンポーネントに自動的に追加されることはありません。 ネストされたマークアップと計算された子要素は現在読み取り専用です。 text-contentはstringフィールドタイプの非推奨の別名です。 すべてのオプションについてはInteractivitySchemaを参照してください。

再利用可能なコンポーネントを接続コンポジションとして登録する

シーンと再利用可能な要素をスタンドアロンのコンポジションとして登録します。Studioはこれらを接続コンポジションと呼びます。これらは独自のタイムラインで開くことができ、親ビデオと同じコンポーネント実装を共有します。

必要なスタイルを定義し、コンポーネント内でフォントをロードして、親なしでも動作するようにします。親からの独立性を参照してください。

親がレンダリングするのと同じエクスポートされたコンポーネント参照を登録します。上記の例では、component={Chapter}を使用し、ChapterInnerやインラインラッパーは使用しません。 Interactive.create()がシーケンスを提供するため、接続のために追加の<Sequence>は必要ありません。

import {Composition} from 'remotion';
import {Chapter} from './Chapter';

export const RemotionRoot = () => (
  <Composition
    id="Chapter"
    component={Chapter}
    width={1280}
    height={720}
    fps={30}
    durationInFrames={90}
    defaultProps={{
      title: 'A new perspective',
      style: {
        backgroundColor: '#111111',
        justifyContent: 'center',
        alignItems: 'center',
      },
    }}
  />
);

一意のID、代表的なインラインのデフォルト値、およびコンポーネントのプレビューに適した寸法、fps、durationInFramesを選択します。登録デフォルト値は、親インスタンスからプロパティを自動的にコピーしません。登録と抽出の詳細については、接続コンポジションを参照してください。

タイミングをコンポーネントに直接配置する

タイミングプロパティをすでに受け入れているコンポーネントの周りに、別の<Sequence>を配置しないようにします。組み込みの対話型コンポーネントとInteractive.create()で作成されたカスタムコンポーネントは、name、from、durationInFrames、trimBefore、playbackRateを直接受け取ることができます。

<Chapter
  name="Opening scene"
  from={30}
  durationInFrames={90}
  style={{opacity: 0.9}}
  title="A new perspective"
/>

共有シーンタイミングの場合は、Interactive.create()で作成されたコンポーネントにコンテンツをグループ化し、そのインスタンスにタイミングを設定します。管理されたルートは、useVideoConfig()用にwidthとheightも受け入れます。

<Track>を使用して、アイテムを同じStudioタイムラインの行に配置します。 シーンが連続している場合は、<TransitionSeries>を使用します。

個別に編集可能なすべてのアイテムに独自のJSXノードを与える

Studioは、アイテムを作成したJSXソースノードを編集します。複数のランタイムアイテムが同じJSXノードから生成される場合、それらは1つのソース編集ターゲットを共有します。

各コンポジション登録、クリップ、シーン、レイヤー、またはシーケンス(個別に編集可能であるべきもの)ごとに、個別のJSXノードを作成し、編集可能なプロパティをそのノードに保持します。これは、<Composition>、<Still>、組み込みメディアコンポーネント、<Sequence>、<TransitionSeries.Sequence>、およびカスタムコンポーネントに適用されます。

たとえば、編集可能なタイムラインを次のように作成します。

import { Track } from "remotion";

<Track name="Scenes">
  <Introduction name="Introduction" durationInFrames={90} />
  <Demo name="Demo" from={90} durationInFrames={150} />
</Track>

.map()やその他のプログラム的なループは、1つのJSXソースノードから複数のランタイムアイテムを作成します。これは、可視化バーやパーティクルなど、1つのテンプレートとして意図的に制御される繰り返し出力に適しています。インスタンスがStudioで独立したIDや名前、プロパティ、メタデータ、タイミング、順序、削除、複製を必要とする場合は適していません。

Interactiveを使用してHTML要素を対話的にする

すべてのHTMLおよびSVG要素(<Img>を除く。すでに対話的です)は、Interactiveを使用して対話的にできます。

<Interactive.Div
  name="Greeting card"
  style={{fontSize: 80, padding: 24}}
>
  Hello
</Interactive.Div>

これにより、Studioでスタイルとキーフレームを設定できます。適切に使用してください。コンポーネントに多くの要素がある場合、タイムラインが乱雑になる可能性があります。

インラインテキストを優先する

テキストが固定されており、1回しか使用されない場合は、定数に抽出する代わりに、インタラクティブ要素内に直接記述します。

// 👍 固定コピーは編集可能なまま
<Interactive.Div name="Title">
  Remotion Best Practices
</Interactive.Div>

テキストが動的または再利用される場合にのみ、プロパティまたは変数を使用します。

インタラクティブ要素にわかりやすい名前を付ける

要素にnameプロパティを追加して、識別しやすくします。 計算された名前は避け、ハードコードします。

<>
  <Interactive.Div name="Hero title" style={{fontSize: 80}}>
    Launch day
  </Interactive.Div>
  <Img name="Avatar" src="https://remotion.media/image.jpeg" />
  <Video name="Background" src="https://remotion.media/video.mp4" />
  <Interactive.Div name="Title">
    Launch day
  </Interactive.Div>
</>

すべてのCSSスタイルをインラインに保つ

最善の方法は、プレーンなオブジェクトをstyleに渡すことです。定数を参照したり、オブジェクトを展開したり、計算を含めたりしないでください。

<Interactive.Div
  style={{
    fontSize: 80,
    color: 'red',
  }}
>
  Hello World!
</Interactive.Div>
const baseStyle = useMemo(() => {
  return {
    fontSize: 12 // ❌ インラインでないスタイルはサポートされません
  }
}, []);

<Interactive.Div
  style={{
    ...baseStyle, // ❌ 展開はサポートされません
    color: RED, // ❌ 定数の参照はサポートされません
    scale: frame * 10 // ❌ 計算はサポートされません
  }}
>
  Hello World!
</Interactive.Div>

interpolate()を使用してアニメーションする

変更するプロパティに対して、インラインのinterpolate()呼び出しとしてアニメーションを記述します。 出力範囲、イージング、外挿、およびoutputプロパティは、ハードコードされた値を使用する必要があります。

入力範囲は、useVideoConfig()から直接分割代入されたdurationInFrames、fps、width、heightを追加で使用できます。durationInFramesなどのベア識別子、2 * fpsやfps * 2などの数値との乗算、およびdurationInFrames - 1などの数値の減算がサポートされています。

const {fps, durationInFrames} = useVideoConfig();

// 👍 インライン値は標準化およびキーフレーム化できます
<Interactive.Div
  name="Product card"
  style={{
    color: 'white',
    fontSize: 80,
    scale: interpolate(frame, [0, fps], [0, 1], {
      easing: Easing.spring({damping: 200}),
      output: 'perceptual-scale',
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp'
    }),
    rotate: interpolate(frame, [0, 1 * fps], ['0deg', '20deg'], {
      easing: Easing.spring({damping: 200}),
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp'
    }),
    translate: interpolate(
      frame,
      [durationInFrames - 30, durationInFrames],
      ['0px 0px', '0px 120px'],
      {
        easing: Easing.spring({damping: 200}),
        output: 'perceptual-scale',
        extrapolateLeft: 'clamp',
        extrapolateRight: 'clamp'
      }
    ),
  }}
/>
const translateY = interpolate(frame, [0, 30], [0, 120]); // ❌ 計算はマークアップに直接記述する必要があります

<Interactive.Div
  name="Product card"
  style={{
    translate: translateY, // ❌ インラインのinterpolate()呼び出しのみサポートされます
    rotate: interpolate(frame, [start, start + 10], [0, Math.PI]), // ❌ 任意の変数での計算や定数の使用はできません
    scale: interpolate(anyVariable, [0, 30], [0, 1]) // ❌ `frame`変数のみ解釈できます。
  }}
/>

Interactive.Pathを使用してSVGパスを編集可能に保つ

SVG内で<path>の代わりにremotionの<Interactive.Path>を使用すると、パスを視覚的に編集できます。

パスの補間とStudioパスキーフレームのために@remotion/pathsをインストールします。

bunx remotion add @remotion/paths

ジオメトリが静的であることが意図されている場合は、パス文字列をdプロパティに直接配置します。定数に抽出しないでください。

import {Interactive} from 'remotion';

<Interactive.Svg width={300} height={300} viewBox="0 0 300 300">
  <Interactive.Path
    name="Triangle"
    d="M 40 40 L 260 40 L 150 260 Z"
    fill="#0b84f3"
  />
</Interactive.Svg>

インラインを使用したパスのモーフィング

…

[翻訳のため省略]

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

関連スキル