Communitygithub.com

Guilhermeslvg/video-portfolio

Remotion マークアップを構造化してインタラクティブにします

video-portfolio とは?

このスキルは、Remotion Studio が認識できる特定の方法で Remotion マークアップを構造化し、コードをインタラクティブにするためのベストプラクティスを提供します。主な機能として、要素のクリック選択、ドラッグ&ドロップ、リサイズ、回転、CSS スタイルの編集、キーフレームとイージング値の編集が可能になります。このスキルは Remotion Studio プラットフォーム向けに設計されており、コンポーネントの `Interactive.withSchema()` を使用したタイムライン管理、スキーマフィールドの定義、接続コンポジションとしてのコンポーネント登録、インラインCSSスタイルと `interpolate()` を使用したアニメーションの記述方法など、詳細なガイドラインを提供します。これにより、開発者は視覚的に編集可能なビデオコンポーネントを作成し、Studio 上で直感的に操作できるようになります。

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

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

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

プレビュー

スキル README より

video-portfolio preview

ドキュメント

Remotion マークアップを特定の方法で記述することで、Remotion Studio はコードの構造を認識し、インタラクティブにすることができます:

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

マークアップが Studio でインタラクティブにするには複雑すぎる場合、値はグレー表示されます。

独自のタイムラインを持つインタラクティブコンポーネントを推奨

親要素を埋めるコンポーネントには Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'}) を使用します。ラッパーはビジュアルルートとタイムラインアイテムを提供し、デフォルトでオーバーフローをクリップするため、内部コンポーネントはフラグメントを返すことができます。タイミング、プリマウント、寸法、インスタンスごとのスタイルはエクスポートされたコンポーネントに設定します。

同じコンポーネント内で一緒に属するビジュアルを保持することで、それらは一緒に移動およびトリミングされます。内部レイアウトコンテナは、アライメント、小さいエリア内でのクリッピング、またはアニメーショングループを提供できます。シーンを埋めるだけのコンテナは通常不要です。カード、テキストボックス、および独自の境界を持つ SVG は、デフォルトの layout: 'none' を維持し、style をビジュアルルートに転送できます。

import {Video} from '@remotion/media';
import type React from 'react';
import {Interactive, useVideoConfig, type InteractivitySchema} from 'remotion';

type SceneProps = {
  readonly title: string;
};

const SceneInner: React.FC<SceneProps> = ({title}) => {
  const {fps} = useVideoConfig();

  return (
    <>
      <Video
        name="背景映像"
        src="https://remotion.media/video.mp4"
        premountFor={fps}
        objectFit="cover"
        style={{width: '100%', height: '100%'}}
      />
      <Interactive.Div
        name="シーンタイトル"
        style={{
          position: 'absolute',
          left: 80,
          bottom: 80,
          color: 'white',
          fontFamily: 'Helvetica, Arial, sans-serif',
          fontSize: 80,
        }}
      >
        {title}
      </Interactive.Div>
    </>
  );
};

const sceneSchema = {
  title: {type: 'string', default: '', description: 'タイトル'},
} as const satisfies InteractivitySchema;

export const Scene = Interactive.withSchema({
  Component: SceneInner,
  componentName: 'Scene',
  schema: sceneSchema,
  wrapInSequence: true,
  layout: 'absolute-fill',
});

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

スキーマ内の各キーは、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 が必要で、description を持つべきです(Studio はそれをラベルとして表示します)。 keyframable: false を設定すると、静的なコントロールが表示されます。

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

ルートの 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 はコンテンツを制御します。 children フラグメントは baseSchema に含まれず、カスタムコンポーネントに自動的に追加されません。 ネストされたマークアップと計算された子は現在読み取り専用です。 text-content は string フィールドタイプの非推奨のエイリアスです。 すべてのオプションについては InteractivitySchema を参照してください。

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

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

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

親がレンダリングするのと同じエクスポートされたコンポーネント参照を登録します。上記の例では、component={Scene} を使用し、SceneInner やインラインラッパーは使用しません。 wrapInSequence: true の場合、接続のために追加の <Sequence> は不要です。

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

export const RemotionRoot = () => (
  <Composition
    id="Scene"
    component={Scene}
    width={1280}
    height={720}
    fps={30}
    durationInFrames={30}
    defaultProps={{
      title: '新しい視点',
    }}
  />
);

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

タイミングを直接コンポーネントに設定

タイミングプロパティを既に受け入れているコンポーネントの周りに別の <Sequence> を配置しないでください。組み込みのインタラクティブコンポーネントや wrapInSequence: true で作成されたカスタムコンポーネントは、name、from、durationInFrames、trimBefore、playbackRate、premountFor を直接受け取ることができます。

<Scene
  name="オープニングシーン"
  from={30}
  durationInFrames={90}
  premountFor={fps}
  style={{opacity: 0.9}}
  title="新しい視点"
/>

冗長なシーケンスを削除する場合は、そのタイミングと名前を子に移動します。既存の子のタイミングを保持します。オフセットを上書きしたり、二重適用したりしないでください。 コンポーネント内の useCurrentFrame() 呼び出しはローカルフレームを読み取りますが、呼び出し箇所でのスタイル式は呼び出し元のフレームを使用します。アニメーションスタイルを移動する際は、その区別を維持します。

複数の兄弟に共有タイミングを提供する場合、タイミングを処理しないコンポーネントをラップする場合、または子が直接受け入れられない寸法を提供する場合は、<Sequence> を維持します。管理ルートは useVideoConfig() 用に width と height も受け入れます。トランジションの有無にかかわらず、連続レイアウトには <TransitionSeries.Sequence> を維持します。直接の from プロパティはこれらの動作を置き換えません。

独立して編集可能な各アイテムに独自の JSX ノードを与える

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

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

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

import { TransitionSeries } from "@remotion/transitions";

<TransitionSeries>
  <TransitionSeries.Sequence name="導入" durationInFrames={90}>
    <Introduction />
  </TransitionSeries.Sequence>
  <TransitionSeries.Sequence name="デモ" durationInFrames={150}>
    <Demo />
  </TransitionSeries.Sequence>
</TransitionSeries>

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

Interactive を使用して HTML 要素をインタラクティブにする

すべての HTML および SVG 要素(<Img> を除く。既にインタラクティブです)は、Interactive を使用してインタラクティブにできます:

<Interactive.Div
  name="グリーティングカード"
  style={{fontSize: 80, padding: 24}}
>
  こんにちは
</Interactive.Div>

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

インラインテキストを推奨

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

// 👍 固定コピーは編集可能なまま
<Interactive.Div name="タイトル">
  Remotion ベストプラクティス
</Interactive.Div>

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

インタラクティブ要素に説明的な名前を付ける

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

<>
  <Interactive.Div name="ヒーロータイトル" style={{fontSize: 80}}>
    発売日
  </Interactive.Div>
  <Img name="アバター" src="https://remotion.media/image.jpeg" />
  <Video name="背景" src="https://remotion.media/video.mp4" />
  <Interactive.Div name="タイトル">
    発売日
  </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="プロダクトカード"
  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({d

Individual skills in this repo

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

関連スキル