Communitygithub.com

ahmadzulfi/video

Strukturiere Remotion-Markup für interaktive Bearbeitung im Studio.

Was ist video?

Diese Fertigkeit definiert einen strukturierten Ansatz für das Schreiben von Remotion-Komponenten, um sie vollständig interaktiv im Remotion Studio zu machen. Sie ermöglicht die Auswahl, das Drag-and-Drop, die Größenänderung und Rotation von Elementen sowie die Bearbeitung von CSS-Stilen, Keyframes und Easing-Werten. Unterstützt werden die Plattformen Remotion Studio und alle darauf aufbauenden Tools. Die Anleitung umfasst die Verwendung von `Interactive.withSchema()` für Komponenten mit eigener Zeitleiste, die Definition von interaktiven Schemata mit verschiedenen Feldtypen (String, Zahl, Farbe, Enum, Array, Asset usw.), das direkte Setzen von Timing-Eigenschaften auf Komponenten und die Verwendung von `interpolate()` für Animationen. Zudem wird empfohlen, jedem einzeln bearbeitbaren Element einen eigenen JSX-Knoten zu geben, interaktive Elemente deskriptiv zu benennen und alle CSS-Stile inline zu halten, um die beste Interaktivität zu gewährleisten.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ahmadzulfi/video/tree/HEAD/.agents/skills/remotion-interactivity

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Vorschau

Aus dem Skill-README

video preview

Dokumentation

Durch das Schreiben von Remotion-Markup in einer bestimmten Weise kann das Remotion Studio die Struktur des Codes erkennen und ihn interaktiv machen:

  • Ermöglicht die Auswahl von Elementen durch Anklicken
  • Ermöglicht Drag-and-Drop, Größenänderung und Rotation
  • Bearbeitung der CSS-Stile
  • Macht Keyframes und Easing-Werte bearbeitbar

Wenn das Markup für das Studio zu komplex ist, um es interaktiv zu machen, werden die Werte ausgegraut.

Bevorzuge interaktive Komponenten mit eigenen Zeitleisten

Verwende Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'}) für Komponenten, die ihren Elterncontainer ausfüllen. Der Wrapper stellt ihre visuelle Wurzel und das Zeitleisten-Element bereit und schneidet standardmäßig Überlauf ab, sodass die innere Komponente ein Fragment zurückgeben kann. Platziere Timing, Vormontage, Abmessungen und pro-Instanz-Stile auf der exportierten Komponente.

Behalte zusammengehörige visuelle Elemente in derselben Komponente, damit sie sich gemeinsam bewegen und trimmen lassen. Ein inneres Layout-Container kann dennoch Ausrichtung, Beschneidung innerhalb eines kleineren Bereichs oder eine animierte Gruppe bereitstellen; ein Container, der nur die Szene ausfüllt, ist normalerweise unnötig. Karten, Textfelder und SVGs mit eigenen Begrenzungen können den Standard layout: 'none' behalten und style an ihre visuelle Wurzel weiterleiten.

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="Hintergrundmaterial"
        src="https://remotion.media/video.mp4"
        premountFor={fps}
        objectFit="cover"
        style={{width: '100%', height: '100%'}}
      />
      <Interactive.Div
        name="Szenentitel"
        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: 'Titel'},
} as const satisfies InteractivitySchema;

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

Schema-Felder auswählen

Jeder Schlüssel im Schema ist eine Eigenschaft, die Studio an der Aufrufstelle lesen und bearbeiten kann. Eigenschaften, die nicht im Schema sind, funktionieren weiterhin, sind aber im Studio nicht bearbeitbar. Schlüssel können die Punktnotation verwenden, z. B. style.color.

TypVerwendung fürKeyframe-fähig
stringZeichenketten, wie Titel, Eingabeaufforderung oder Code-AusschnittNein
numberZahlen, mit optionalen min, max, step, integerJa
booleanEin/Aus-SchalterJa (halten)
colorCSS-FarbzeichenkettenJa
enumEine Auswahl zwischen variants, jeweils mit eigenem verschachteltem SchemaOpt-in (halten)
arrayListen von Zahlen, Farben, Enums und mehrNein
assetMedienquellen, mit optionalem assetTypeNein
font-familyCSS-SchriftfamilieNein
font-weightSchriftgewichtJa
translate, scale, rotation-css, rotation-degrees, transform-originTransformationenJa
uv-coordinateEin normalisierter [x, y]-Punkt auf dem ElementJa
svg-pathSVG-PfaddatenJa
remotion-captionsCaption[]-DatenNein
hiddenEin Wert, der außerhalb der Steuerungen gehalten wird—

Jedes Feld benötigt einen default-Wert und sollte eine description haben, die Studio als Bezeichnung anzeigt. Setze keyframable: false, um ein statisches Steuerelement anzuzeigen.

Lege Inhalt und pro-Instanz-Erscheinungsbild offen, z. B. den Text und eine Akzentfarbe. Füge keine Felder für style.translate, style.scale, style.rotate, style.transformOrigin und style.opacity hinzu: wrapInSequence: true fügt sie bereits hinzu.

Um CSS-Eigenschaften der Wurzel pro Instanz bearbeitbar zu machen, verteile die integrierten Schema-Fragmente:

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

Ebenfalls verfügbar: Interactive.borderSchema und Interactive.captionsSchema für Komponenten, die Untertitel akzeptieren. Für Komponenten, die React.ReactNode-Children akzeptieren, füge ...Interactive.childrenSchema hinzu. Es bietet Steuerelemente für den Inhalt von Children und kann sich weiterentwickeln, um reichhaltigeren Inhalt zu unterstützen. Derzeit wird ein Zeichenkettenfeld mit der Bezeichnung "Text" und einem Standardwert von '' verwendet. Wenn eine Komponente nur Zeichenketten als Children erfordert, deklariere stattdessen ein explizites children-Feld mit type: 'string'. Interactive.textSchema steuert typografische Stile; childrenSchema steuert den Inhalt. Das Children-Fragment ist nicht in baseSchema enthalten und wird nicht automatisch zu benutzerdefinierten Komponenten hinzugefügt. Verschachteltes Markup und berechnete Children sind derzeit schreibgeschützt. text-content ist ein veralteter Alias für den Feldtyp string. Siehe InteractivitySchema für alle Optionen.

Registriere wiederverwendbare Komponenten als verbundene Kompositionen

Registriere umfangreiche Szenen und wiederverwendbare Komponenten mit eigenen Ebenen oder Animationen als eigenständige Kompositionen. Studio nennt diese verbundene Kompositionen: Sie können in ihrer eigenen Zeitleiste geöffnet werden, während sie dieselbe Komponentenimplementierung mit dem übergeordneten Video teilen.

Definiere erforderliche Stile und lade Schriftarten innerhalb der Komponente, damit sie ohne ihr Elternelement funktioniert. Siehe Elternunabhängigkeit.

Registriere denselben exportierten Komponentenverweis, den das Elternelement rendert. Für das obige Beispiel verwende component={Scene}, nicht SceneInner oder einen Inline-Wrapper. Mit wrapInSequence: true ist kein zusätzliches <Sequence> für die Verbindung erforderlich.

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: 'Eine neue Perspektive',
    }}
  />
);

Wähle eine eindeutige ID, repräsentative Inline-Standardwerte und für die Vorschau der Komponente geeignete Abmessungen, fps und Dauer. Registrierungsstandardwerte kopieren nicht automatisch Eigenschaften von einer übergeordneten Instanz. Siehe verbundene Kompositionen für Details zur Registrierung und Extraktion.

Platziere Timing direkt auf der Komponente

Vermeide ein separates <Sequence> um eine Komponente, die bereits Timing-Eigenschaften akzeptiert. Eingebaute interaktive Komponenten und benutzerdefinierte Komponenten, die mit wrapInSequence: true erstellt wurden, können name, from, durationInFrames, trimBefore, playbackRate und premountFor direkt empfangen.

<Scene
  name="Eröffnungsszene"
  from={30}
  durationInFrames={90}
  premountFor={fps}
  style={{opacity: 0.9}}
  title="Eine neue Perspektive"
/>

Wenn ein redundantes Sequence entfernt wird, verschiebe sein Timing und seinen Namen auf das Kind. Bewahre vorhandenes Kind-Timing; überschreibe oder verdopple keine Offsets nicht. Ein useCurrentFrame()-Aufruf innerhalb der Komponente liest ihren lokalen Frame, während ein Stilausdruck an der Aufrufstelle noch den Frame des Aufrufers verwendet. Behalte diese Unterscheidung beim Verschieben von Animationsstilen bei.

Behalte ein <Sequence> bei, wenn es gemeinsames Timing für mehrere Geschwister bereitstellt, eine Komponente umschließt, die kein Timing verarbeitet, oder Abmessungen bereitstellt, die das Kind nicht direkt akzeptieren kann. Managed Roots akzeptieren auch width und height für useVideoConfig(). Behalte <TransitionSeries.Sequence> für aufeinanderfolgendes Layout bei, mit oder ohne Übergänge; direkte from-Eigenschaften ersetzen diese Verhaltensweisen nicht.

Gib jedem einzeln bearbeitbaren Element einen eigenen JSX-Knoten

Das Studio bearbeitet den JSX-Quellknoten, der ein Element erstellt hat. Wenn mehrere Laufzeitelemente vom selben JSX-Knoten stammen, teilen sie sich ein Quellbearbeitungsziel.

Für jede Kompositionsregistrierung, jeden Clip, jede Szene, Ebene oder Sequenz, die eigenständig bearbeitbar sein soll, schreibe einen separaten JSX-Knoten und behalte seine bearbeitbaren Eigenschaften auf diesem Knoten. Dies gilt für <Composition>, <Still>, eingebaute Medienkomponenten, <Sequence>, <TransitionSeries.Sequence> und benutzerdefinierte Komponenten.

Erstelle beispielsweise eine bearbeitbare Zeitleiste wie folgt:

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

<TransitionSeries>
  <TransitionSeries.Sequence name="Einleitung" durationInFrames={90}>
    <Introduction />
  </TransitionSeries.Sequence>
  <TransitionSeries.Sequence name="Demo" durationInFrames={150}>
    <Demo />
  </TransitionSeries.Sequence>
</TransitionSeries>

Ein .map() oder eine andere programmatische Schleife würde mehrere Laufzeitelemente aus einem JSX-Quellknoten erstellen. Das ist für wiederholte Ausgaben geeignet, die bewusst als eine Vorlage gesteuert werden, wie Visualisierungsbalken oder Partikel. Es ist nicht geeignet, wenn die Instanzen unabhängige IDs oder Namen, Eigenschaften, Metadaten, Timing, Reihenfolge, Löschung oder Duplizierung im Studio benötigen.

Mache ein HTML-Element mit Interactive interaktiv

Jedes HTML- und SVG-Element (außer <Img>, es ist bereits interaktiv) wie <div> kann mit Interactive interaktiv gemacht werden:

<Interactive.Div
  name="Grußkarte"
  style={{fontSize: 80, padding: 24}}
>
  Hallo
</Interactive.Div>

Dies ermöglicht das Setzen von Stilen und Keyframes im Studio. Sei bedacht: Wenn eine Komponente viele Elemente hat, könnte die Zeitleiste unübersichtlich werden.

Bevorzuge Inline-Text

Wenn Text festgelegt und nur einmal verwendet wird, schreibe ihn direkt innerhalb des interaktiven Elements, anstatt ihn in eine Konstante zu extrahieren.

// 👍 Fester Text bleibt bearbeitbar
<Interactive.Div name="Titel">
  Remotion Best Practices
</Interactive.Div>

Verwende eine Eigenschaft oder Variable nur, wenn der Text dynamisch oder wiederverwendet wird.

Gib interaktiven Elementen einen beschreibenden Namen

Füge eine name-Eigenschaft zu Elementen hinzu, um sie leicht identifizierbar zu machen. Vermeide berechnete Namen, hartcodiere sie.

<>
  <Interactive.Div name="Heldentitel" style={{fontSize: 80}}>
    Starttag
  </Interactive.Div>
  <Img name="Avatar" src="https://remotion.media/image.jpeg" />
  <Video name="Hintergrund" src="https://remotion.media/video.mp4" />
  <Interactive.Div name="Titel">
    Starttag
  </Interactive.Div>
</>

Behalte alle CSS-Stile inline

Der beste Weg ist, einfach ein einfaches Objekt an style zu übergeben – ohne Verweis auf Konstanten, ohne Objekt-Spreading, ohne Mathematik.

<Interactive.Div
  style={{
    fontSize: 80,
    color: 'red',
  }}
>
  Hallo Welt!
</Interactive.Div>
const baseStyle = useMemo(() => {
  return {
    fontSize: 12 // ❌ Nicht-inline Stile werden nicht unterstützt
  }
}, []);

<Interactive.Div
  style={{
    ...baseStyle, // ❌ Spread wird nicht unterstützt
    color: RED, // ❌ Verweis auf Konstanten wird nicht unterstützt
    scale: frame * 10 // ❌ Mathematik wird nicht unterstützt
  }}
>
  Hallo Welt!
</Interactive.Div>

Animiere mit interpolate()

Schreibe Animationen als Inline-interpolate()-Aufrufe auf der sich ändernden Eigenschaft. Der Ausgabebereich, das Easing, die Extrapolation und die output-Eigenschaft sollten hartcodierte Werte verwenden.

Der Eingabebereich kann zusätzlich durationInFrames, fps, width und height verwenden, die direkt aus useVideoConfig() destrukturiert werden. Bloße Bezeichner wie durationInFrames, Multiplikation mit einer Zahl wie 2 * fps oder fps * 2 und Subtraktion einer Zahl wie durationInFrames - 1 werden unterstützt.

const {fps, durationInFrames} = useVideoConfig();

// 👍 Inline-Werte können standardisiert und mit Keyframes versehen werden
<Interactive.Div
  name="Produktkarte"
  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}),
        extrapolateLeft: 'clamp',
        extrapolateRight: 'clamp'
      }
    ),
  }}
>
  Hallo Welt!
</Interactive.Div>

Wenn das Studio interpolate() nicht erkennen kann, werden der Wert und die Keyframe-Steuerung ausgegraut. Vermeide es, Hilfsfunktionen aufzurufen, die interpolate() enthalten, da das Studio diese dann nicht erkennen kann.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Verwandte Skills