Communitygithub.com

jeffersonmendo/remotion-tryslides

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

Was ist remotion-tryslides?

Dieses Skill definiert, wie Remotion-Komponenten und -Markup strukturiert werden müssen, damit die Remotion Studio-Oberfläche sie vollständig interaktiv erkennen und bearbeiten kann. Es ermöglicht Anklicken, Ziehen und Ablegen, Größenänderung, Rotation, CSS-Stilbearbeitung sowie die direkte Manipulation von Keyframes und Easing-Werten. Das Skill behandelt die Nutzung von `Interactive.create()`, `Track` und verbundenen Kompositionen (Connected Compositions), die Auswahl von Schema-Feldern für die Studio-Steuerung, die separate Registrierung wiederverwendbarer Komponenten als eigenständige Kompositionen und die korrekte Platzierung von Timing-Props direkt auf der Komponente. Zudem wird erklärt, wie man pro unabhängig editierbarem Element einen eigenen JSX-Knoten erstellt, HTML-Elemente mit `Interactive` interaktiv macht, CSS-Styles inline hält, Animationen mit `interpolate()` direkt im Markup definiert und SVG-Pfade mit `Interactive.Path` editierbar hält. Es unterstützt Entwickler, die mit Remotion, Remotion Studio, TypeScript und JSX arbeiten, um eine maximal interaktive und benutzerfreundliche Video-Editierungsumgebung zu schaffen.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/jeffersonmendo/remotion-tryslides/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

remotion-tryslides preview

Dokumentation

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

  • Ermöglicht das Auswählen von Elementen durch Anklicken
  • Ermöglicht Drag & Drop, Größenänderung und Rotation
  • Bearbeiten von CSS-Stilen
  • Keyframes und Easing-Werte werden editierbar

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

Erstelle neue Kompositionen mit Track, verbundenen Kompositionen und Interactive.create().

Setze layout explizit auf 'absolute-fill' für Szenen oder 'none' für begrenzte Elemente. Registriere dieselben exportierten Szenen- und wiederverwendbaren Elementkomponenten mit Composition.

Beginne mit dem vollständigen Track + verbundene Kompositionen Beispiel. Es enthält das Element, die Szene, die übergeordnete Zeitleiste und die Root-Registrierungen.

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

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

  return (
    <Track name="Kapitel">
      <Chapter
        name="Eröffnung"
        durationInFrames={3 * fps}
        title="Einleitung"
      />
      <Chapter
        name="Funktion"
        from={3 * fps}
        durationInFrames={4 * fps}
        title="Neue Funktionen"
      />
    </Track>
  );
};

Füge Hintergrund- und Ausrichtungsstile auf jeder Szeneninstanz ein, wie im vollständigen Beispiel gezeigt. Behalte reine Layout-Container als einfaches HTML bei.

Schema-Felder auswählen

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

TypVerwendungKeyframable
stringZeichenketten, wie Titel, Prompt oder Code-SnippetNein
numberZahlen, mit optionalen min, max, step, integerJa
booleanEin/Aus-SchalterJa (halten)
colorCSS-FarbstringsJa
enumEine Auswahl zwischen variants, jede mit eigenem verschachtelten SchemaOpt-in (halten)
arrayListen von Zahlen, Farben, Enums und mehrNein
assetMedienquellen, mit optionalem assetTypeNein
font-familyCSS-SchriftfamilieNein
font-weightSchriftstärkeJa
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 aus den Steuerungen ausgeschlossen wird—

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

Lege Inhalt und instanzspezifisches Aussehen offen, wie Text und eine Akzentfarbe. Füge keine Felder für style.translate, style.scale, style.rotate, style.transformOrigin und style.opacity hinzu: Interactive.create() fügt sie automatisch hinzu.

Um CSS-Eigenschaften des Roots pro Instanz editierbar zu machen, verwende 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 Captions akzeptieren. Für Komponenten, die React.ReactNode-Children akzeptieren, füge ...Interactive.childrenSchema hinzu. Es bietet Steuerungen für den Inhalt von Children und kann sich weiterentwickeln, um reichhaltigere Inhalte zu unterstützen. Derzeit verwendet es ein String-Feld mit der Bezeichnung "Text" und einem Standardwert von ''. Wenn eine Komponente nur String-Children erfordert, deklariere stattdessen ein explizites children-Feld mit type: 'string'. Interactive.textSchema steuert Typografiestile; 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.

Wiederverwendbare Komponenten als verbundene Kompositionen registrieren

Registriere Szenen und wiederverwendbare Elemente als eigenständige Kompositionen. Das 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 übergeordnetes Element funktioniert. Siehe Elternunabhängigkeit.

Registriere dieselbe exportierte Komponentenreferenz, die das übergeordnete Element rendert. Verwende für das obige Beispiel component={Chapter}, nicht ChapterInner oder einen Inline-Wrapper. Interactive.create() stellt die Sequenz bereit, sodass kein zusätzliches <Sequence> für die Verbindung erforderlich ist.

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: 'Eine neue Perspektive',
      style: {
        backgroundColor: '#111111',
        justifyContent: 'center',
        alignItems: 'center',
      },
    }}
  />
);

Wähle eine eindeutige ID, repräsentative Inline-Standardwerte sowie geeignete Abmessungen, fps und Dauer für die Vorschau der Komponente. Registrierungsstandards kopieren nicht automatisch Props von einer übergeordneten Instanz. Siehe verbundene Kompositionen für Registrierungs- und Extraktionsdetails.

Timing direkt auf der Komponente platzieren

Vermeide ein separates <Sequence> um eine Komponente, die bereits Timing-Props akzeptiert. Integrierte interaktive Komponenten und benutzerdefinierte Komponenten, die mit Interactive.create() erstellt wurden, können name, from, durationInFrames, trimBefore und playbackRate direkt empfangen.

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

Für gemeinsames Szenen-Timing gruppiere den Inhalt in einer Komponente, die mit Interactive.create() erstellt wurde, und timing diese Instanz. Verwaltete Roots akzeptieren auch width und height für useVideoConfig().

Verwende <Track>, um Elemente in dieselbe Studio-Zeitleistenzeile zu setzen. Verwende <TransitionSeries>, wenn Szenen sequenziell sind.

Gib jedem unabhängig editierbaren Element seinen eigenen JSX-Knoten

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

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

Erstelle beispielsweise eine editierbare Zeitleiste wie folgt:

import { Track } from "remotion";

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

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

Ein HTML-Element mit Interactive interaktiv machen

Jedes HTML- und SVG-Element (außer <Img>, das bereits interaktiv ist), 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 Festlegen von Stilen und Keyframes im Studio. Sei vernünftig: Wenn eine Komponente viele Elemente hat, könnte die Zeitleiste unübersichtlich werden.

Bevorzuge Inline-Text

Wenn Text fest ist und nur einmal verwendet wird, schreibe ihn direkt in das interaktive Element, anstatt ihn in eine Konstante auszulagern.

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

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

Gib interaktiven Elementen einen beschreibenden Namen

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

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

Alle CSS-Stile inline halten

Der beste Weg ist, einfach ein einfaches Objekt an style zu übergeben – ohne Verweise 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 Styles werden nicht unterstützt
  }
}, []);

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

Mit interpolate() animieren

Schreibe Animationen als Inline-interpolate()-Aufrufe auf der Eigenschaft, die sich ändert. 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 keyframed 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}),
        output: 'perceptual-scale',
        extrapolateLeft: 'clamp',
        extrapolateRight: 'clamp'
      }
    ),
  }}
/>
const translateY = interpolate(frame, [0, 30], [0, 120]); // ❌ Mathematik sollte direkt im Markup sein

<Interactive.Div
  name="Produktkarte"
  style={{
    translate: translateY, // ❌ Nur Inline interpolate()-Aufrufe werden unterstützt,
    rotate: interpolate(frame, [start, start + 10], [0, Math.PI]), // ❌ Kann keine Mathematik mit beliebigen Variablen verwenden, keine Konstanten
    scale: interpolate(anyVariable, [0, 30], [0, 1]) // ❌ Kann nur die `frame`-Variable interpretieren.
  }}
/>

SVG-Pfade mit Interactive.Path editierbar halten

Verwende <Interactive.Path> aus remotion anstelle von <path> innerhalb eines SVG, um den Pfad visuell editierbar zu machen.

Installiere @remotion/paths für Pfadinterpolation und Studio-Pfad-Keyframes:

bunx remotion add @remotion/paths

Wenn die Geometrie statisch sein soll, setze den Pfadstring direkt in die d-Prop. Extrahiere ihn nicht in eine Konstante.

import {Interactive} from 'remotion';

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

Pfade mit interpolate() morphen

...

[Abgeschnitten für Übersetzung]

Individual skills in this repo

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

Verwandte Skills