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.
| Typ | Verwendung für | Keyframe-fähig |
|---|---|---|
string | Zeichenketten, wie Titel, Eingabeaufforderung oder Code-Ausschnitt | Nein |
number | Zahlen, mit optionalen min, max, step, integer | Ja |
boolean | Ein/Aus-Schalter | Ja (halten) |
color | CSS-Farbzeichenketten | Ja |
enum | Eine Auswahl zwischen variants, jeweils mit eigenem verschachteltem Schema | Opt-in (halten) |
array | Listen von Zahlen, Farben, Enums und mehr | Nein |
asset | Medienquellen, mit optionalem assetType | Nein |
font-family | CSS-Schriftfamilie | Nein |
font-weight | Schriftgewicht | Ja |
translate, scale, rotation-css, rotation-degrees, transform-origin | Transformationen | Ja |
uv-coordinate | Ein normalisierter [x, y]-Punkt auf dem Element | Ja |
svg-path | SVG-Pfaddaten | Ja |
remotion-captions | Caption[]-Daten | Nein |
hidden | Ein 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.
