Communitygithub.com

AlexGit92s/Remotion2.0

Estructura el marcado de Remotion para la interactividad en el Estudio de Remotion.

¿Qué es Remotion2.0?

Esta habilidad define las mejores prácticas para escribir marcado de Remotion de manera que el Estudio de Remotion pueda reconocer la estructura del código y hacerlo interactivo. Esto incluye permitir seleccionar elementos haciendo clic, arrastrar y soltar, redimensionar, rotar, editar estilos CSS y hacer que los keyframes y valores de easing sean editables. La habilidad proporciona pautas detalladas sobre cómo usar `Interactive.withSchema({wrapInSequence: true})` para componentes personalizados, cómo elegir los tipos de campo de esquema adecuados (como `text-content`, `color`, `number`), y cómo registrar componentes reutilizables como composiciones conectadas. También cubre cómo animar usando `interpolate()` directamente en el marcado, mantener los estilos CSS en línea y dar a los elementos interactivos nombres descriptivos. Siguiendo estas pautas, los desarrolladores pueden garantizar que sus componentes de Remotion sean totalmente editables en el Estudio de Remotion, agilizando el proceso de creación de vídeos programáticos. La habilidad es útil para cualquier desarrollador que utilice Remotion para crear animaciones y vídeos con componentes interactivos, y es compatible con el Estudio de Remotion como plataforma principal.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/AlexGit92s/Remotion2.0/tree/HEAD/.claude/skills/remotion-interactivity

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Vista previa

Del README del skill

Remotion2.0 preview

Documentación

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.

TipoUsar paraKeyframable
text-contentTexto, como un títuloNo
numberNúmeros, con min, max, step, integer opcionalesSí
booleanInterruptores de encendido/apagadoSí (mantener)
colorCadenas de color CSSSí
enumUna elección entre variants, cada uno con su propio esquema anidadoOpt-in (mantener)
arrayListas de números, colores, enums y másNo
assetFuentes de medios, con assetType opcionalNo
font-familyFamilia de fuente CSSNo
font-weightPeso de fuenteSí
translate, scale, rotation-css, rotation-degrees, transform-originTransformacionesSí
uv-coordinateUn punto [x, y normalizado en el elementoSí
svg-pathDatos de ruta SVGSí
remotion-captionsDatos Caption[]No
hiddenUn 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.

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Skills relacionados