Indem man Remotion-Markup auf eine bestimmte Weise schreibt, kann der Remotion Studio die Struktur des Codes erkennen und ihn interaktiv machen:
- Auswählen von Elementen durch Klicken
- Drag-and-Drop, Größenänderung und Rotation
- Bearbeiten von CSS-Stilen
- Keyframes und Easing-Werte bearbeitbar machen
Wenn das Markup zu komplex für den Studio ist, um es interaktiv zu machen, werden die Werte ausgegraut.
Interaktive Komponenten mit eigenen Zeitleisten bevorzugen
Verwenden Sie Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'}) für Komponenten, die ihr Elternelement ausfüllen. Der Wrapper stellt ihre visuelle Wurzel und das Zeitleistenelement bereit und schneidet standardmäßig Überläufe ab, sodass die innere Komponente ein Fragment zurückgeben kann. Setzen Sie Timing, Vorladen, Abmessungen und instanzspezifische Stile auf die exportierte Komponente.
Behalten Sie visuelle Elemente, die zusammengehören, in derselben Komponente, damit sie sich zusammen bewegen und trimmen lassen. Ein inneres Layout-Container kann dennoch Ausrichtung, Zuschneiden innerhalb eines kleineren Bereichs oder eine animierte Gruppe bieten; ein Container, der nur die Szene ausfüllt, ist normalerweise unnötig. Karten, Textfelder und SVGs mit eigenen Grenzen 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 der Studio an der Aufrufstelle lesen und bearbeiten kann. Eigenschaften, die nicht im Schema sind, funktionieren trotzdem, sind aber im Studio nicht bearbeitbar. Schlüssel können Punktnotation verwenden, wie z. B. style.color.
| Typ | Verwendung für | Keyframing möglich |
|---|---|---|
string | Zeichenketten, z. B. Titel, Aufforderung oder Code-Snippet | 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, jede mit eigenem verschachtelten 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 | Schriftstärke | 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 Bedienelemente gehalten wird | — |
Jedes Feld benötigt einen default-Wert und sollte eine description haben, die der Studio als Bezeichnung anzeigt. Setzen Sie keyframable: false, um ein statisches Bedienelement anzuzeigen.
Machen Sie den Inhalt und das Erscheinungsbild pro Instanz verfügbar, wie z. B. den Text und eine Akzentfarbe. Fügen Sie 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, verwenden Sie die integrierten Schemafragmente:
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ügen Sie ...Interactive.childrenSchema hinzu. Es bietet Bedienelemente für den Inhalt der Children und könnte sich weiterentwickeln, um reichhaltigeren Inhalt zu unterstützen. Derzeit verwendet es ein Zeichenkettenfeld mit der Bezeichnung "Text" und einem Standardwert von ''. Wenn eine Komponente nur Zeichenketten als Children erfordert, deklarieren Sie 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 Connected Compositions registrieren
Registrieren Sie wichtige Szenen und wiederverwendbare Komponenten mit eigenen Ebenen oder Animationen als eigenständige Kompositionen. Der Studio nennt diese Connected Compositions: Sie können in ihrer eigenen Zeitleiste geöffnet werden, während sie dieselbe Komponentenimplementierung mit dem übergeordneten Video teilen.
Definieren Sie erforderliche Stile und laden Sie Schriftarten innerhalb der Komponente, damit sie ohne ihr übergeordnetes Element funktioniert. Siehe übergeordnete Unabhängigkeit. Registrieren Sie dieselbe exportierte Komponentenreferenz, die das übergeordnete Element rendert. Verwenden Sie für das obige Beispiel 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ählen Sie eine eindeutige ID, repräsentative Inline-Standardwerte sowie für die Vorschau der Komponente geeignete Abmessungen, fps und Dauer. Registrierungsstandardwerte kopieren nicht automatisch Eigenschaften von einer übergeordneten Instanz. Siehe Connected Compositions für Details zur Registrierung und Extraktion.
Timing direkt auf der Komponente platzieren
Vermeiden Sie ein separates <Sequence> um eine Komponente, die bereits Timing-Eigenschaften akzeptiert. Integrierte 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"
/>
Beim Entfernen einer redundanten Sequenz verschieben Sie deren Timing und Namen zum Kind. Bewahren Sie vorhandenes Timing des Kindes; überschreiben oder verdoppeln Sie Offsets nicht. Ein useCurrentFrame()-Aufruf innerhalb der Komponente liest ihren lokalen Frame, während ein Stilausdruck an der Aufrufstelle den Frame des Aufrufers verwendet. Bewahren Sie diese Unterscheidung beim Verschieben von Animationsstilen.
Behalten Sie ein <Sequence> bei, wenn es gemeinsames Timing für mehrere Geschwister bereitstellt, eine Komponente umschließt, die kein Timing verarbeitet, oder Abmessungen liefert, die das Kind nicht direkt akzeptieren kann. Verwaltete Wurzeln akzeptieren auch width und height für useVideoConfig(). Behalten Sie <TransitionSeries.Sequence> für aufeinanderfolgendes Layout, mit oder ohne Übergänge; direkte from-Eigenschaften ersetzen diese Verhaltensweisen nicht.
Jedes unabhängig bearbeitbare Element erhält seinen eigenen JSX-Knoten
Der Studio bearbeitet den JSX-Quellknoten, der ein Element erstellt hat. Wenn mehrere Laufzeitelemente vom selben JSX-Knoten stammen, teilen sie sich ein Quellbearbeitungsziel.
Schreiben Sie für jede Kompositionsregistrierung, jeden Clip, jede Szene, jede Ebene oder Sequenz, die eigenständig bearbeitbar sein soll, einen separaten JSX-Knoten und behalten Sie deren bearbeitbare Eigenschaften auf diesem Knoten. Dies gilt für <Composition>, <Still>, integrierte Medienkomponenten, <Sequence>, <TransitionSeries.Sequence> und benutzerdefinierte Komponenten.
Erstellen Sie 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. Dies ist für wiederholte Ausgaben geeignet, die absichtlich als eine Vorlage gesteuert werden, wie z. B. 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.
Ein HTML-Element mit Interactive interaktiv machen
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. Seien Sie jedoch umsichtig: Wenn eine Komponente viele Elemente hat, könnte die Zeitleiste unübersichtlich werden.
Inline-Text bevorzugen
Wenn Text festgelegt und nur einmal verwendet wird, schreiben Sie ihn direkt innerhalb des interaktiven Elements, anstatt ihn in eine Konstante zu extrahieren.
// 👍 Fester Kopie bleibt bearbeitbar
<Interactive.Div name="Titel">
Remotion Best Practices
</Interactive.Div>
Verwenden Sie eine Eigenschaft oder Variable nur, wenn der Text dynamisch oder wiederverwendet wird.
Interaktiven Elementen einen beschreibenden Namen geben
Fügen Sie eine name-Eigenschaft zu Elementen hinzu, um sie leicht identifizierbar zu machen. Vermeiden Sie berechnete Namen, codieren Sie sie fest.
<>
<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>
</>
Alle CSS-Stile inline halten
Der beste Weg ist, 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>
Mit interpolate() animieren
Schreiben Sie Animationen als Inline-interpolate()-Aufrufe auf der sich ändernden Eigenschaft.
Der Ausgabebereich, das Easing, die Extrapolation und die output-Eigenschaft sollten fest codierte Werte verwenden.
Der Eingabebereich kann zusätzlich durationInFrames, fps, width und height verwenden, die direkt aus useVideoConfig() destrukturiert wurden. 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}),
