Al escribir el marcado de Remotion de una manera específica, el Estudio de Remotion es capaz de reconocer la estructura del código y hacerlo interactivo:
- Permitir seleccionar elementos haciendo clic en ellos
- Permitir arrastrar y soltar, redimensionar y rotar
- Editar los estilos CSS
- Hacer que los keyframes y valores de easing sean editables
Si el marcado es demasiado complejo para que el Estudio lo haga interactivo, los valores se vuelven grises.
Prefiere componentes interactivos con sus propias líneas de tiempo
Usa Interactive.withSchema({wrapInSequence: true}) para escenas, tarjetas,
títulos y otros componentes visuales personalizados cuyas props deban ser editables por instancia.
Expone controles de contenido y apariencia significativos en un InteractivitySchema.
Mantén los detalles de implementación decorativos dentro del componente.
El componente debe aceptar style y pasarlo a una raíz visual. Mantén una
raíz compartida cuando las transformaciones, el recorte o la opacidad deban afectar a todas sus capas.
Aplana envoltorios internos redundantes cuando sus estilos se puedan mover a un
elemento existente sin cambiar el diseño o la animación.
import type React from 'react';
import {Interactive, type InteractivitySchema} from 'remotion';
type LowerThirdProps = {
readonly children: string;
readonly accentColor: string;
readonly style?: React.CSSProperties;
};
const LowerThirdInner: React.FC<LowerThirdProps> = ({
children,
accentColor,
style,
}) => {
return (
<Interactive.Div
style={{
position: 'absolute',
left: 80,
bottom: 80,
display: 'flex',
alignItems: 'center',
gap: 20,
backgroundColor: 'white',
borderRadius: 16,
padding: '20px 32px',
color: 'black',
fontFamily: 'Helvetica, Arial, sans-serif',
fontSize: 48,
fontWeight: 600,
...style,
}}
>
<div
style={{
width: 8,
alignSelf: 'stretch',
borderRadius: 4,
backgroundColor: accentColor,
}}
/>
{children}
</Interactive.Div>
);
};
const lowerThirdSchema = {
children: {type: 'text-content', default: '', description: 'Texto'},
accentColor: {
type: 'color',
default: '#0b84f3',
description: 'Color de acento',
},
} as const satisfies InteractivitySchema;
export const LowerThird = Interactive.withSchema({
Component: LowerThirdInner,
componentName: '<LowerThird>',
schema: lowerThirdSchema,
wrapInSequence: true,
});
Pasar el style inyectado dentro del componente es obligatorio; mantén los estilos
editables en línea en el lugar de uso del componente.
Elegir campos del esquema
Cada clave en el esquema es una prop que el Estudio puede leer y editar en el lugar de uso.
Las props que no están en el esquema aún funcionan, pero no son editables en el Estudio.
Las claves pueden usar notación de punto, como style.color.
| Tipo | Usar para | Keyframable |
|---|---|---|
text-content | Texto, como un título | No |
number | Números, con min, max, step, integer opcionales | Sí |
boolean | Interruptores de encendido/apagado | Sí (mantener) |
color | Cadenas de color CSS | Sí |
enum | Una elección entre variants, cada uno con su propio esquema anidado | Opt-in (mantener) |
array | Listas de números, colores, enums y más | No |
asset | Fuentes de medios, con assetType opcional | No |
font-family | Familia de fuente CSS | No |
font-weight | Peso de fuente | Sí |
translate, scale, rotation-css, rotation-degrees, transform-origin | Transformaciones | Sí |
uv-coordinate | Un punto [x, y normalizado en el elemento | Sí |
svg-path | Datos de ruta SVG | Sí |
remotion-captions | Datos Caption[] | No |
hidden | Un valor que se mantiene fuera de los controles | — |
Cada campo necesita un default y debe tener un description, que el Estudio muestra como etiqueta.
Configura keyframable: false para mostrar un control estático.
Expone el contenido y la apariencia por instancia, como el texto y un color de acento.
No agregues campos para style.translate, style.scale, style.rotate, style.transformOrigin y style.opacity: wrapInSequence: true ya los añade.
Para hacer que las propiedades CSS de la raíz sean editables por instancia, extiende los fragmentos de esquema integrados:
const cardSchema = {
...Interactive.textSchema, // style.color, style.fontSize, style.fontWeight, ...
...Interactive.backgroundSchema, // style.backgroundColor
...Interactive.borderRadiusSchema, // style.borderRadius, ...
} as const satisfies InteractivitySchema;
También están disponibles: Interactive.borderSchema, e Interactive.captionsSchema para componentes que aceptan subtítulos.
Consulta InteractivitySchema para todas las opciones.
Registrar componentes reutilizables como composiciones conectadas
Registra escenas sustanciales y componentes reutilizables con sus propias capas o animación como composiciones independientes. El Estudio llama a estas composiciones conectadas: se pueden abrir en su propia línea de tiempo mientras comparten la misma implementación de componente con el vídeo padre.
Define los estilos necesarios y carga las fuentes dentro del componente para que funcione sin su padre. Consulta independencia del padre.
Registra la misma referencia de componente exportado que el padre renderiza. Para el
ejemplo anterior, usa component={LowerThird}, no LowerThirdInner o un envoltorio en línea.
Con wrapInSequence: true, no se necesita un <Sequence> adicional para la conexión.
import {Composition} from 'remotion';
import {LowerThird} from './LowerThird';
export const RemotionRoot = () => (
<Composition
id="LowerThird"
component={LowerThird}
width={1280}
height={720}
fps={30}
durationInFrames={30}
defaultProps={{
children: 'Jane Doe, Diseñadora de Producto',
accentColor: '#0b84f3',
}}
/>
);
Elige un ID único, valores predeterminados en línea representativos y dimensiones, fps y duración adecuados para previsualizar el componente. Los valores predeterminados de registro no copian automáticamente las props de una instancia padre. Consulta composiciones conectadas para detalles de registro y extracción.
Colocar el tiempo directamente en el componente
Evita un <Sequence> separado alrededor de un componente que ya acepte props de
tiempo. Los componentes interactivos integrados y los componentes personalizados creados con
wrapInSequence: true pueden recibir name, from, durationInFrames,
trimBefore, playbackRate y premountFor directamente.
<LowerThird
name="Tercer inferior"
from={30}
durationInFrames={90}
accentColor="#0b84f3"
>
Jane Doe, Diseñadora de Producto
</LowerThird>
Al eliminar una secuencia redundante, mueve su tiempo y nombre al hijo.
Preserva cualquier tiempo existente del hijo; no sobrescribas ni apliques desplazamientos dobles.
Una llamada a useCurrentFrame() dentro del componente lee su fotograma local, mientras que una
expresión de estilo en el lugar de uso aún usa el fotograma de la persona que llama. Preserva esa
distinción al mover estilos de animación.
Mantén un <Sequence> cuando proporcione tiempo compartido para múltiples hermanos,
sobrescriba las dimensiones para useVideoConfig(), o envuelva un componente que no maneje
tiempo. Mantén <Series.Sequence> para diseño consecutivo y
<TransitionSeries.Sequence> para transiciones; las props from directas no
reemplazan esos comportamientos.
Dale a cada elemento editable individualmente su propio nodo JSX
El Estudio edita el nodo fuente JSX que creó un elemento. Si varios elementos en tiempo de ejecución provienen del mismo nodo JSX, comparten un único destino de edición de fuente.
Para cada registro de composición, clip, escena, capa o secuencia que deba ser
editable por sí mismo, escribe un nodo JSX separado y mantén sus props editables
en ese nodo. Esto se aplica a <Composition>, <Still>, componentes multimedia
integrados, <Sequence>, <Series.Sequence>, <TransitionSeries.Sequence>
y componentes personalizados.
Por ejemplo, crea una línea de tiempo editable así:
<Series>
<Series.Sequence name="Introducción" durationInFrames={90}>
<Introduction />
</Series.Sequence>
<Series.Sequence name="Demo" durationInFrames={150}>
<Demo />
</Series.Sequence>
</Series>
Un .map() u otro bucle programático crearía múltiples elementos en tiempo de ejecución
a partir de un nodo fuente JSX. Eso es apropiado para resultados repetidos que se
controlan intencionalmente como una plantilla, como barras de visualización o
partículas. No es apropiado cuando las instancias necesitan IDs o
nombres, props, metadatos, tiempo, orden, eliminación o duplicación independientes en el
Estudio.
Haz que un elemento HTML sea interactivo usando Interactive
Cada elemento HTML y SVG (excepto <Img>, que ya es interactivo) como <div> se puede convertir en interactivo usando Interactive:
<Interactive.Div
name="Tarjeta de saludo"
style={{fontSize: 80, padding: 24}}
>
Hola
</Interactive.Div>
Esto permite que los estilos y keyframes se configuren en el Estudio. Sé sensato, si un componente tiene muchos elementos, la línea de tiempo podría volverse desordenada.
Prefiere texto en línea
Si el texto es fijo y solo se usa una vez, escríbelo directamente dentro del elemento interactivo en lugar de extraerlo en una constante.
// 👍 El texto fijo permanece editable
<Interactive.Div name="Título">
Mejores Prácticas de Remotion
</Interactive.Div>
Usa una prop o variable solo cuando el texto sea dinámico o se reutilice.
Dale a los elementos interactivos un nombre descriptivo
Agrega una prop name a los elementos para hacerlos fácilmente identificables.
Evita nombres calculados, codifícalos.
<>
<Interactive.Div name="Título principal" style={{fontSize: 80}}>
Día de lanzamiento
</Interactive.Div>
<Img name="Avatar" src="https://remotion.media/image.jpeg" />
<Video name="Fondo" src="https://remotion.media/video.mp4" />
<Interactive.Div name="Título">
Día de lanzamiento
</Interactive.Div>
</>
Mantén todos los estilos CSS en línea
La mejor manera es simplemente pasar un objeto plano a style - sin referirse a constantes, sin expandir objetos, sin matemáticas.
<Interactive.Div
style={{
fontSize: 80,
color: 'red',
}}
>
¡Hola Mundo!
</Interactive.Div>
const baseStyle = useMemo(() => {
return {
fontSize: 12 // ❌ Los estilos no en línea no son compatibles
}
}, []);
<Interactive.Div
style={{
...baseStyle, // ❌ Expandir no es compatible
color: RED, // ❌ Referirse a constantes no es compatible
scale: frame * 10 // ❌ Las matemáticas no son compatibles
}}
>
¡Hola Mundo!
</Interactive.Div>
Animar usando interpolate()
Escribe animaciones como llamadas interpolate() en línea sobre la propiedad que cambia.
El rango de salida, easing, extrapolación y la propiedad output deben usar valores codificados.
El rango de entrada puede usar además durationInFrames, fps, width y height desestructurados directamente de useVideoConfig(). Los identificadores simples como durationInFrames, la multiplicación con un número como 2 * fps o fps * 2, y la resta de un número como durationInFrames - 1 son compatibles.
const {fps, durationInFrames} = useVideoConfig();
// 👍 Los valores en línea se pueden estandarizar y keyframear
<Interactive.Div
name="Tarjeta de producto"
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]); // ❌ Las matemáticas deben estar directamente en el marcado
<Interactive.Div
name="Tarjeta de producto"
style={{
translate: translateY, // ❌ Solo se admiten llamadas interpolate() en línea,
rotate: interpolate(frame, [start, start + 10], [0, Math.PI]), // ❌ No se pueden usar matemáticas con variables arbitrarias, no se pueden usar constantes
scale: interpolate(frame, [0, durationInFrames], [1, 2], {
easing: easingFunction, // ❌ El easing debe ser un valor literal
output: 'perceptual-scale',
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
}),
top: interpolate(
frame,
[0, 0.5 * durationInFrames],
[0, 100],
{
easing: Easing.bezier(0.25, 0.1, 0.25, 1), // ❌ Easing.bezier no es compatible, usa Easing.inOut(Easing.ease)
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
},
),
}}
/>
Usa solo fragmentos de esquema compatibles
Usa solo los tipos de campo y propiedades del esquema compatibles. El Estudio de Remotion se basa en que la estructura del marcado coincida con las capacidades del esquema. El uso de fragmentos no compatibles o la configuración de props en los elementos de manera que el esquema no pueda analizar provocará que los controles aparezcan atenuados o que la interactividad falle.
Para una referencia completa de los tipos de campo del esquema y sus propiedades, consulta la
documentación de InteractivitySchema.
