Communitygithub.com

Guilhermeslvg/video-portfolio

コンテンツ、アニメーション、エフェクトのベストプラクティス

video-portfolio とは?

このスキルは、Remotionを使用した動画制作における、コンテンツ、アニメーション、エフェクトに関するベストプラクティスを提供します。Reactコンポーネントベースの動画編集フレームワークであるRemotionにおいて、ユーザーの変更を保持し、Studioでの編集を容易にするための具体的なコード例とパターンを示しています。主なトピックとして、`useCurrentFrame()`と`interpolate()`を用いたドライブアニメーション、CSS transitionやアニメーションクラスを使わない方針、`Interactive.withSchema`を用いたコンポーネント設計、アセットやメディアコンポーネントの適切な配置と使用法、エフェクトの適用方法などが含まれます。また、複数シーンの動画、接続コンポジション、トランジション、モーションブラー、テキストハイライト、音声ナレーションなど、より高度なトピックについても言及しています。Claude Code、Cursor、CodexなどのAIエージェントがRemotionプロジェクトを支援する際に、正確で編集可能なコードを生成するためのガイドラインとして機能します。

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

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

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

プレビュー

スキル README より

ドキュメント

これはRemotion Reactマークアップのガイダンスです。 これに関連しない場合は、代わりにRemotionベストプラクティスを読み込んでください。

ユーザーの変更を保持する

ユーザーは会話の外でコードを編集する場合があります。 その間に予期しない変更が検出された場合は、上書きせず、意図的なものと想定するか、確認を求めてください。

一般的なルール

コンポジションまたはシーンコンポーネント内では、useVideoConfig()からfpsを取得し、すべての秒数からフレームへの計算(from、trimBefore、durationInFrames、premountForなどのタイミングプロパティを含む)に使用します。JSXノード上で式を保持することで(例:trimBefore={4 * fps})、Studioがタイミングを編集でき、コンポジションフレームレートが変更された場合でも正しさが保たれます。

アニメーションはuseCurrentFrame()とinterpolate()を使用して駆動します。
CSSのtransitionやanimationは正しくレンダリングされないため、リファクタリングする必要があります。
Tailwindのアニメーションクラスは正しくレンダリングされないため、リファクタリングする必要があります。

タイミングをカスタマイズするには、Easing.bezier()とEasing.spring()を使用します。

マークアップはRemotionインタラクティブベストプラクティスに従って構成します。 編集可能なプロパティを持つシーンには、Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'})を推奨します。これらの内部マークアップはフラグメントを返すことができ、ラッパーがレイアウトとインスタンススタイルを処理します。ローワーサードなどのサイズ指定要素はデフォルトのレイアウトを維持し、styleを自身のルートに転送できます。再利用可能なシーンは接続コンポジションとして登録します。 タイミングはそれをサポートするコンポーネントに直接配置し、冗長な<Sequence>ラッパーは避けます。 premountForをサポートするすべてのタイミングコンポーネントに、1秒間のプリマウントを指定します:premountFor={fps}(fpsはuseVideoConfig()から取得)。これをメディア、インタラクティブコンポーネント、<Sequence>、<TransitionSeries.Sequence>、<TransitionSeries.Overlay>に適用し、シーン内にネストされたタイミングコンポーネントも含めます。ネストされたアイテムが親の開始前にマウントする必要がある場合は、親のタイムラインアイテムもプリマウントします。premountForを持たないコンポーネント(<TransitionSeries.Transition>など)は、置き換える必要はありません。

Studioは、アイテムを作成したJSXソースノードを編集します。すべてのコンポジション登録、クリップ、シーン、レイヤー、シーケンスは、それぞれ独立して編集可能なJSXノードとして作成し、編集可能なプロパティをインラインに配置します。 プログラムによるループは、生成されたインスタンスが1つのソーステンプレートとして意図的に制御される場合に適しており、ユーザーがインスタンスを個別に編集する必要がある場合には適していません。

import { useCurrentFrame, useVideoConfig, Easing, interpolate, Interactive } from "remotion";

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

  return (
    <Interactive.Div
      name="Title"
      premountFor={fps}
      style={{
        opacity: interpolate(frame, [0, 2 * fps], [0, 1], {
          extrapolateRight: "clamp",
          extrapolateLeft: "clamp",
          easing: Easing.bezier(0.16, 1, 0.3, 1),
        }),
      }}
    >
      Hello World!
    </Interactive.Div>
  );
};

interpolate()の呼び出しはstyleプロパティ内にインラインで保持します。 transformの代わりにscale、translate、rotateのCSSプロパティを使用します。

// 👍 インラインで編集可能なキーフレームとtransformの省略形
style={{
  scale: interpolate(frame, [0, 100], [0, 1], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
    output: 'perceptual-scale' // `scale`アニメーションでは、"output: 'perceptual-scale'"を使用
  }),
  translate: interpolate(frame, [0, 100], ["0px 0px", "100px 100px"], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
  }),
  rotate: interpolate(frame, [0, 100], ["20deg", "90deg"], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
    easing: Easing.spring({damping: 200}),
  }),
}}

// 👎 インラインでない値とtransform文字列はStudioでの編集が難しくなる
const scale = interpolate(frame, [0, 100], [0, 1]);

style={{
  transform: `scale(${scale})`,
}}

アセット

アセットはプロジェクトルートのpublic/フォルダに配置します。 public/フォルダのファイルを参照するには、staticFile()を使用します。

メディアコンポーネント

動画と音声は@remotion/mediaの<Video>と<Audio>を使用して追加します。
画像は<CanvasImage>コンポーネントを使用して追加します。 アニメーションGIF、APNG、WebP、AVIF画像は<AnimatedImage>を使用して追加します。Chromeを使用しない場合は、@remotion/gifを使用します。 public/内のファイルにはstaticFile()を使用するか、リモートURLを直接渡します:

import { Audio, Video } from "@remotion/media";
import { staticFile, CanvasImage, AnimatedImage, useVideoConfig } from "remotion";

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

  return (
    <>
      <Video src={staticFile("video.mp4")} premountFor={fps} style={{ opacity: 0.5 }} />
      <Audio src={staticFile("audio.mp3")} premountFor={fps} />
      <CanvasImage
        src={staticFile("logo.png")}
        premountFor={fps}
        style={{ width: 100, height: 100 }}
      />
      <Video src="https://remotion.media/video.mp4" premountFor={fps} />
      <AnimatedImage src={staticFile('nyancat.gif')} premountFor={fps} />
    </>
  );
};

コンポジションが主にビデオまたはオーディオクリップのタイムラインである場合は、ソース構造を選択する前にvideo-editing.mdを読んでください。

シーンの例

背景ビデオとローワーサード。 ローワーサードは独自のタイムラインを持つインタラクティブコンポーネントであり、接続コンポジションとして登録されています。 そのテキストはchildrenとして渡され、accentColorは編集可能なプロパティです。 フェードインは呼び出し箇所でインラインでキーフレーム設定されています。

// MyScene.tsx
import { Video } from "@remotion/media";
import { Easing, interpolate, useCurrentFrame, useVideoConfig } from "remotion";
import { LowerThird } from "./LowerThird";

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

  return (
    <>
      <Video
        name="Background"
        src="https://remotion.media/video.mp4"
        premountFor={fps}
        objectFit="cover"
        style={{
          position: "absolute",
          width: "100%",
          height: "100%",
        }}
      />
      <LowerThird
        name="Lower third"
        from={1 * fps}
        premountFor={fps}
        accentColor="#0b84f3"
        style={{
          opacity: interpolate(frame, [1 * fps, 2 * fps], [0, 1], {
            extrapolateRight: "clamp",
            extrapolateLeft: "clamp",
            easing: Easing.bezier(0.16, 1, 0.3, 1),
          }),
        }}
      >
        Jane Doe, Product Designer
      </LowerThird>
    </>
  );
};
// LowerThird.tsx
import type React from "react";
import { Interactive, type InteractivitySchema } from "remotion";

type LowerThirdProps = {
  readonly children: React.ReactNode;
  readonly accentColor: string;
  readonly style?: React.CSSProperties;
};

const LowerThirdInner: React.FC<LowerThirdProps> = ({
  children,
  accentColor,
  style,
}) => {
  return (
    <Interactive.Div
      style={{
        position: "absolute",
        left: 80,
        bottom: 80,
        display: "flex",
        alignItems: "center",
        gap: 20,
        backgroundColor: "white",
        borderRadius: 16,
        padding: "20px 32px",
        color: "black",
        fontFamily: "Helvetica, Arial, sans-serif",
        fontSize: 48,
        fontWeight: 600,
        ...style,
      }}
    >
      <div
        style={{
          width: 8,
          alignSelf: "stretch",
          borderRadius: 4,
          backgroundColor: accentColor,
        }}
      />
      {children}
    </Interactive.Div>
  );
};

const lowerThirdSchema = {
  ...Interactive.childrenSchema,
  accentColor: {
    type: "color",
    default: "#0b84f3",
    description: "アクセントカラー",
  },
} as const satisfies InteractivitySchema;

export const LowerThird = Interactive.withSchema({
  Component: LowerThirdInner,
  componentName: "LowerThird",
  schema: lowerThirdSchema,
  wrapInSequence: true,
});
import { Composition } from "remotion";
import { LowerThird } from "./LowerThird";
import { MyScene } from "./MyScene";

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="MyScene"
        component={MyScene}
        durationInFrames={60}
        fps={30}
        width={1280}
        height={720}
      />
      <Composition
        id="LowerThird"
        component={LowerThird}
        durationInFrames={30}
        fps={30}
        width={1280}
        height={720}
        defaultProps={{
          children: "Jane Doe, Product Designer",
          accentColor: "#0b84f3",
        }}
      />
    </>
  );
};

遅延とトリミング

タイミングはそれをサポートするコンポーネントに直接配置します。サポートされているプロパティ、例、および操作の順序については、Timing propsを参照してください。

マップ

動画にマップを含めたい場合は、Remotion Mapsを参照してください。

テキストハイライトと注釈

テキストハイライト(ハイライトマーカー)、円、下線、取り消し線、打ち消し線、ボックスについては、text-highlights.mdを参照してください。

マルチシーンビデオ

複数の連続したシーンで動画を制作する予定がある場合は、multi-scene-video.mdを参照してください。

接続コンポジション

シーンまたはレイヤーのグループに独自の編集可能なタイムラインを持たせる場合は、connected-compositions.mdに従ってください。マルチシーンビデオの重要なシーンには、この構造を推奨します。

プリコンポーズアクション

Pre-compose Ambient glow (src/BarChart.tsx:134) のようなStudioリクエストに対しては、指定された場所にある選択されたシーケンスマークアップを見つけます。connected-compositions.mdに従って接続コンポジションを作成します。マークアップを名前付きコンポーネントに抽出し、できればInteractive.withSchema({wrapInSequence: true})でインタラクティブにし、同じエクスポートされたコンポーネント参照をルート内の一意の<Composition>で登録します。インタラクティブコンポーネントをそのタイミングプロパティとともに直接レンダリングするか、ラッパーに目的がある場合はシーケンスの唯一の子としてレンダリングします。選択されたノードがすでにシーケンスである場合は、そのプロパティを保持し、子を抽出します。登録には、親の使用と同等の寸法、fps、期間、およびdefaultPropsが必要です。登録されたコンポジションなしのコンポーネント抽出は、プリコンポーズリクエストを完了しません。

継承されたスタイル(fontFamilyやフォントの読み込みなど)を抽出されたコンポーネントに引き継ぎます。親からの独立性を参照してください。

ナレーション

ElevenLabs TTSを使用してAI生成ナレーションをRemotionコンポジションに追加するには、voiceover.mdを参照してください。

ビデオの埋め込み

ビデオの埋め込みに関する高度な知識(トリミング、ボリューム、速度、ループ、ピッチ)については、embedding-videos.mdを参照してください。

音声の埋め込み

トリミング、ボリューム、速度、ピッチなどの高度な音声機能については、audio.mdを参照してください。

クロッピング

コンポーネントの表示矩形をクロップする必要がある場合は、cropping.mdを参照してください。

トランジション

シーントランジションパターンについては、transitions.mdを参照してください。

モーションブラー

モーションブラーや移動軌跡を追加する場合は、優先されるHTML-in-canvasアプローチ、プレビューの要件、および代替案についてmotion-blur.mdを読んでください。

ビジュアルエフェクトとピクセルエフェクト

ビジュアルエフェクトを追加する場合、まずそれが影響を与える要素またはグループを特定します。 ターゲットコンポーネントがeffectsをサポートしており、以下の組み込みエフェクトがリクエストに一致する場合は、CSSによる近似よりもそのエフェクトを優先します。例えば、ビネットがそのビデオに影響を与えるべき場合、<Video>のeffectsプロパティに直接vignette()を適用します。

以下のコンポーネントは、effectsを直接受け入れます:

インポート元コンポーネント
remotion<Img>, <CanvasImage>, <AnimatedImage>, <Solid>, <HtmlInCanvas>
@remotion/media<Video>
@remotion/gif<Gif>
@remotion/rive<RemotionRiveCanvas>
@remotion/shapes<Arrow>, <Callout>, <Circle>, <Ellipse>, <Heart>, <Pie>, <Polygon>, <Rect>, <Spark>, <Star>, <Triangle>

通常のHTML要素、<Interactive.*> DOMラッパー、<AbsoluteFill>、<Sequence>は、effectsを直接受け入れません。それらのコンテンツにエフェクトを適用するには、<HtmlInCanvas>でラップし、ラッパーにeffectsプロパティを配置します。

他の要素については、適切な場合は通常のHTMLとCSSを優先します。グループ全体の後処理が適切であり、環境がサポートしている場合は、グループを<HtmlInCanvas>でラップすることを検討してください。

エフェクトを適用する前に、インポート、パラメータ、セットアップについてeffects.mdを読んでください。組み込みエフェクトが一致しない場合の、createEffect()を使用した再利用可能なカスタムエフェクトについても説明しています。

利用可能なエフェクト

@remotion/effectsからの組み込みエフェクト:

  • 色とトーン: brightness(), colorCorrection(), colorKey(), contrast(), duotone(), exposure(), grayscale(), hue(), invert(), levels(), linearGradient(), linearGradientTint(), lut(), saturation(), shadowsHighlights(), thermalVision(), tint(), vibrance(), whiteBalance().
  • ブラーと軌跡: blur(), lightTrail(), linearProgressiveBlur(), radialProgressiveBlur(), regionBlur(), shadow().
  • ディストーション: bulge(), lensDistortion(), perspective(), pinch(), twirl().
  • スタイル化: chromaticAberration(), colorReplace(), edgeDetection(), glitch(), grain(), halftone(), mirror(), neon(), noise(), outline(), pixelate(), posterize(), ripple(), vignette().

@remotion/effects-transitionsからのトランジションエフェクト:

linearWipe(), slide(), iris(), clock(), pageTurn().

@remotion/lottieからのLottieエフェクト:

利用可能なLottieエフェクトのリストについては、Lottieのドキュメントを参照してください。

補間の出力タイプ

補間の出力タイプについては、interpolation-outputs.mdを参照してください。

パスアニメーション

パスアニメーションについては、svg-paths-animation.mdを参照してください。

アニメーションカーブ(イージング)

アニメーションカーブ(イージング)については、easing-curves.mdを参照してください。

環境

環境については、environment.mdを参照してください。

トラブルシューティング

トラブルシューティングについては、troubleshooting.mdを参照してください。

パフォーマンス

パフォーマンスのヒントについては、performance.mdを参照してください。

セキュリティ

セキュリティについては、security.mdを参照してください。

API

APIについては、api.mdを参照してください。

CLI

CLIについては、cli.mdを参照してください。

Individual skills in this repo

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

関連スキル