En écrivant le balisage Remotion d'une manière spécifique, le Remotion Studio est capable de reconnaître la structure du code et de le rendre interactif :
- Permettre de sélectionner des éléments en cliquant dessus
- Permettre le glisser-déposer, le redimensionnement et la rotation
- Modifier les styles CSS
- Rendre les images clés et les valeurs d'easing modifiables
Si le balisage est trop complexe pour que le Studio le rende interactif, les valeurs deviennent grisées.
Préférer les composants interactifs avec leurs propres timelines
Utilisez Interactive.withSchema({wrapInSequence: true, layout: 'absolute-fill'}) pour les composants qui remplissent leur parent. Le wrapper fournit leur racine visuelle et leur élément de timeline, et coupe le dépassement par défaut, afin que le composant interne puisse retourner un fragment. Placez le timing, le pré-montage, les dimensions et les styles par instance sur le composant exporté.
Gardez les visuels qui vont ensemble dans le même composant, afin qu'ils se déplacent et se découpent ensemble. Un conteneur de mise en page interne peut toujours fournir l'alignement, le rognage dans une zone plus petite, ou un groupe animé ; un conteneur qui ne fait que remplir la scène est généralement inutile. Les cartes, les zones de texte et les SVG avec leurs propres limites peuvent conserver la valeur par défaut layout: 'none' et transmettre style à leur racine visuelle.
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="Background footage"
src="https://remotion.media/video.mp4"
premountFor={fps}
objectFit="cover"
style={{width: '100%', height: '100%'}}
/>
<Interactive.Div
name="Scene title"
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: 'Title'},
} as const satisfies InteractivitySchema;
export const Scene = Interactive.withSchema({
Component: SceneInner,
componentName: 'Scene',
schema: sceneSchema,
wrapInSequence: true,
layout: 'absolute-fill',
});
Choisir les champs du schéma
Chaque clé dans le schéma est une prop que le Studio peut lire et éditer sur le site d'appel.
Les props qui ne sont pas dans le schéma fonctionnent toujours, mais ne sont pas modifiables dans le Studio.
Les clés peuvent utiliser la notation par points, comme style.color.
| Type | Utilisation | Keyframable |
|---|---|---|
string | Chaînes de caractères, comme un titre, une invite ou un extrait de code | Non |
number | Nombres, avec min, max, step, integer facultatifs | Oui |
boolean | Interrupteurs marche/arrêt | Oui (maintien) |
color | Chaînes de couleurs CSS | Oui |
enum | Un choix entre variants, chacun avec son propre schéma imbriqué | Optionnel (maintien) |
array | Listes de nombres, couleurs, énumérations et plus | Non |
asset | Sources média, avec assetType facultatif | Non |
font-family | Famille de polices CSS | Non |
font-weight | Graisse de police | Oui |
translate, scale, rotation-css, rotation-degrees, transform-origin | Transformations | Oui |
uv-coordinate | Un point normalisé [x, y] sur l'élément | Oui |
svg-path | Données de chemin SVG | Oui |
remotion-captions | Données Caption[] | Non |
hidden | Une valeur gardée hors des contrôles | — |
Chaque champ a besoin d'une valeur par default et devrait avoir une description, que le Studio affiche comme libellé.
Définissez keyframable: false pour afficher un contrôle statique.
Exposez le contenu et l'apparence par instance, comme le texte et une couleur d'accentuation.
N'ajoutez pas de champs pour style.translate, style.scale, style.rotate, style.transformOrigin et style.opacity : wrapInSequence: true les ajoute déjà.
Pour rendre les propriétés CSS de la racine modifiables par instance, étendez les fragments de schéma intégrés :
const cardSchema = {
...Interactive.textSchema, // style.color, style.fontSize, style.fontWeight, ...
...Interactive.backgroundSchema, // style.backgroundColor
...Interactive.borderRadiusSchema, // style.borderRadius, ...
} as const satisfies InteractivitySchema;
Également disponibles : Interactive.borderSchema, et Interactive.captionsSchema pour les composants qui acceptent des légendes.
Pour les composants qui acceptent des enfants React.ReactNode, ajoutez ...Interactive.childrenSchema.
Il fournit des contrôles pour le contenu des enfants et peut évoluer pour prendre en charge un contenu plus riche.
Actuellement, il utilise un champ de chaîne intitulé "Text" avec une valeur par défaut de ''.
Si un composant nécessite des enfants de type chaîne uniquement, déclarez un champ children explicite avec type: 'string' à la place.
Interactive.textSchema contrôle les styles typographiques ; childrenSchema contrôle le contenu.
Le fragment enfants n'est pas inclus dans baseSchema ni automatiquement ajouté aux composants personnalisés.
Le balisage imbriqué et les enfants calculés sont actuellement en lecture seule.
text-content est un alias obsolète pour le type de champ string.
Voir InteractivitySchema pour toutes les options.
Enregistrer les composants réutilisables en tant que compositions connectées
Enregistrez les scènes importantes et les composants réutilisables avec leurs propres couches ou animation en tant que compositions autonomes. Le Studio les appelle compositions connectées : elles peuvent être ouvertes dans leur propre timeline tout en partageant la même implémentation de composant avec la vidéo parente.
Définissez les styles requis et chargez les polices à l'intérieur du composant pour qu'il fonctionne sans son parent. Voir l'indépendance parentale.
Enregistrez la même référence de composant exporté que celle rendue par le parent. Pour l'exemple
ci-dessus, utilisez component={Scene}, pas SceneInner ou un wrapper en ligne.
Avec wrapInSequence: true, aucun <Sequence> supplémentaire n'est nécessaire pour la connexion.
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: 'A new perspective',
}}
/>
);
Choisissez un ID unique, des valeurs par défaut représentatives en ligne, et des dimensions, fps et durée adaptés à la prévisualisation du composant. Les valeurs par défaut de l'enregistrement ne copient pas automatiquement les props d'une instance parente. Voir compositions connectées pour les détails d'enregistrement et d'extraction.
Placer le timing directement sur le composant
Évitez un <Sequence> séparé autour d'un composant qui accepte déjà les props de timing.
Les composants interactifs intégrés et les composants personnalisés créés avec
wrapInSequence: true peuvent recevoir name, from, durationInFrames,
trimBefore, playbackRate, et premountFor directement.
<Scene
name="Opening scene"
from={30}
durationInFrames={90}
premountFor={fps}
style={{opacity: 0.9}}
title="A new perspective"
/>
Lors de la suppression d'une séquence redondante, déplacez son timing et son nom vers l'enfant.
Préservez tout timing enfant existant ; n'écrasez pas et n'appliquez pas deux fois les décalages.
Un appel useCurrentFrame() à l'intérieur du composant lit son image locale, tandis qu'une
expression de style sur le site d'appel utilise toujours l'image de l'appelant. Préservez cette
distinction lors du déplacement des styles d'animation.
Gardez un <Sequence> lorsqu'il fournit un timing partagé pour plusieurs frères,
enveloppe un composant qui ne gère pas le timing, ou fournit des dimensions que l'enfant
ne peut pas accepter directement. Les racines gérées acceptent également width et height pour
useVideoConfig(). Gardez <TransitionSeries.Sequence> pour une mise en page consécutive,
avec ou sans transitions ; les props from directes ne remplacent pas ces comportements.
Donner à chaque élément modifiable indépendamment son propre nœud JSX
Le Studio modifie le nœud source JSX qui a créé un élément. Si plusieurs éléments au moment de l'exécution proviennent du même nœud JSX, ils partagent une seule cible d'édition de source.
Pour chaque enregistrement de composition, clip, scène, calque ou séquence qui devrait
être modifiable individuellement, écrivez un nœud JSX séparé et conservez ses props modifiables
sur ce nœud. Cela s'applique à <Composition>, <Still>, les composants média intégrés,
<Sequence>, <TransitionSeries.Sequence>
et les composants personnalisés.
Par exemple, créez une timeline modifiable comme ceci :
import { TransitionSeries } from "@remotion/transitions";
<TransitionSeries>
<TransitionSeries.Sequence name="Introduction" durationInFrames={90}>
<Introduction />
</TransitionSeries.Sequence>
<TransitionSeries.Sequence name="Demo" durationInFrames={150}>
<Demo />
</TransitionSeries.Sequence>
</TransitionSeries>
Un .map() ou une autre boucle programmatique créerait plusieurs éléments d'exécution
à partir d'un seul nœud source JSX. Cela est approprié pour une sortie répétée qui est
intentionnellement contrôlée comme un seul modèle, comme des barres de visualisation ou des
particules. Ce n'est pas approprié lorsque les instances ont besoin d'identifiants ou de noms,
de props, de métadonnées, de timing, d'ordre, de suppression ou de duplication indépendants dans le
Studio.
Rendre un élément HTML interactif à l'aide de Interactive
Tout élément HTML et SVG (sauf <Img>, il est déjà interactif) comme <div> peut être rendu interactif à l'aide de Interactive :
<Interactive.Div
name="Greeting card"
style={{fontSize: 80, padding: 24}}
>
Hello
</Interactive.Div>
Cela permet de définir des styles et des images clés dans le Studio. Soyez raisonnable : si un composant a de nombreux éléments, la timeline peut devenir désordonnée.
Préférer le texte en ligne
Si le texte est fixe et utilisé une seule fois, écrivez-le directement à l'intérieur de l'élément interactif au lieu de l'extraire dans une constante.
// 👍 La copie fixe reste modifiable
<Interactive.Div name="Title">
Remotion Best Practices
</Interactive.Div>
Utilisez une prop ou une variable uniquement lorsque le texte est dynamique ou réutilisé.
Donner aux éléments interactifs un nom descriptif
Ajoutez une prop name aux éléments pour les rendre facilement identifiables.
Évitez les noms calculés, codez-les en dur.
<>
<Interactive.Div name="Hero title" style={{fontSize: 80}}>
Launch day
</Interactive.Div>
<Img name="Avatar" src="https://remotion.media/image.jpeg" />
<Video name="Background" src="https://remotion.media/video.mp4" />
<Interactive.Div name="Title">
Launch day
</Interactive.Div>
</>
Garder tous les styles CSS en ligne
La meilleure façon est de simplement passer un objet simple à style - sans faire référence à des constantes, sans déstructuration d'objet, sans calcul.
<Interactive.Div
style={{
fontSize: 80,
color: 'red',
}}
>
Hello World!
</Interactive.Div>
const baseStyle = useMemo(() => {
return {
fontSize: 12 // ❌ Les styles non inline ne sont pas pris en charge
}
}, []);
<Interactive.Div
style={{
...baseStyle, // ❌ La déstructuration n'est pas prise en charge
color: RED, // ❌ La référence à des constantes n'est pas prise en charge
scale: frame * 10 // ❌ Les calculs ne sont pas pris en charge
}}
>
Hello World!
</Interactive.Div>
Animer à l'aide de interpolate()
Écrivez les animations sous forme d'appels interpolate() en ligne sur la propriété qui change.
La plage de sortie, l'easing, l'extrapolation et la propriété output doivent utiliser des valeurs codées en dur.
La plage d'entrée peut également utiliser durationInFrames, fps, width et height déstructurés directement à partir de useVideoConfig(). Les identifiants simples tels que durationInFrames, la multiplication par un nombre comme 2 * fps ou fps * 2, et la soustraction d'un nombre comme durationInFrames - 1 sont pris en charge.
const {fps, durationInFrames} = useVideoConfig();
// 👍 Les valeurs en ligne peuvent être standardisées et animées par images clés
<Interactive.Div
name="Product card"
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'
}
),
}}
>
Hello World!
</Interactive.Div>
Lorsque l'utilisation de interpolate() est trop complexe ou illisible, envisagez d'utiliser les props d'entrée from et durationInFrames et de déléguer l'animation au composant.
Définir des durées constantes dans le schéma
Lorsqu'une durée est fixe et devrait être visible dans le Studio comme une métadonnée en lecture seule, transmettez-la au schéma en tant que number avec l'option constant: true et une default. Utilisez keyframable: false pour garantir qu'elle n'apparaît pas dans les contrôles.
const schema = {
compositionDurationInFrames: {
type: 'number',
default: 150,
constant: true,
keyframable: false,
description: 'Duration',
},
} as const satisfies InteractivitySchema;
Ceci n'affecte pas le calcul du timing ; il s'agit uniquement de métadonnées.
Déduplication des métadonnées
Évitez d'utiliser des valeurs calculées pour name, from, durationInFrames, premountFor, playbackRate, width, height et trimBefore. Codez-les en dur si possible. Si une valeur calculée est inévitable, le Studio essaie de l'évaluer. Gardez les valeurs calculées simples : utilisez uniquement des identifiants de premier niveau et des constantes ; n'utilisez pas d'appels de fonction, d'opérateurs ternaires ou de références à d'autres variables.
// 👍 Bien : l'évaluation peut identifier la valeur
<Scene durationInFrames={fps * 2} />
// ❌ L'évaluation ne peut pas identifier la valeur
<Scene durationInFrames={someFunction(fps)} />
// 👍 Utilisez plutôt une constante
<Scene durationInFrames={120} />
// 👍 Les valeurs déstructurées sont également acceptables
<Scene durationInFrames={durationInFrames} />
