Animación explicativa
Un video es una lista ordenada de escenas en src/timeline.ts. Tres tipos:
intro: la tarjeta de enganche. Su primera línea es el enganche, se muestra grande y sus palabras se iluminan a medida que se pronuncian (sirve como su propio subtítulo).anim: un componente de animación del registro, sincronizado con las líneas de narración de la escena.outro: la tarjeta de cierre con la insignia de AGILabs,javieraguilar.aiy el título del artículo deSETTINGS.articleTitle.
Escribir la línea de tiempo
import type { Cuts } from './types.ts';
import { L, anim, intro, outro, say, videos } from './narrate.ts';
const build = (): Cuts => ({
main: [
intro('hook', [say(0.4, 'EN hook line.', 'ES hook line.')]),
anim(
'result',
'BarCompare',
{ bars: [{ label: L('With the reason', 'Con el porqué'), value: 14, display: '14/20', highlight: true },
{ label: L('Without it', 'Sin él'), value: 4, display: '4/20' }], max: 20 },
[say(0.3, 'First line.', 'Primera línea.'), say(0, 'Second line.', 'Segunda línea.')],
{ min: 4 },
),
outro('outro', [say(0.4, 'More at javieraguilar.ai.', 'Más en javieraguilar.ai.')]),
],
});
export const VIDEOS = videos(build);
export const TIMELINE = VIDEOS.en.main;
say(at, en, es, spoken?): una línea de narración.ates el segundo más temprano de la escena en que puede comenzar; las líneas nunca se superponen (espacio de 0.35 s), y la escena dura hasta que su última línea termina más 0.8 s, omin.spoken({ en?, es? }) es lo que lee el TTS cuando difiere del texto del subtítulo (vernarration).- Pulsos. El tiempo de inicio de cada línea en una escena es un pulso. Los componentes reproducen el paso i en el pulso i (la barra i crece, el nodo i aparece, el mosaico i aparece), así que escribe una línea por paso que quieras revelar. Antes de TTS, los pulsos provienen de una estimación de duración; después de
npm run ttsprovienen del audio real, por lo que debes volver a revisar los fotogramas después de narrar. L(en, es): una etiqueta bilingüe en pantalla. Una cadena de texto simple se usa para ambos idiomas.- Los identificadores de escena deben ser únicos: las claves de línea (
<escena>-01, ...) nombran los archivos de audio. opts.chapterenanimse almacena en la escena pero aún no es dibujado por ningún componente.- Ejecuta
npm run lintdespués de cada cambio: verifica la longitud contratargetSeconds(±20 %), las señales de subtítulos con menos de 0.8 s o más de 17 caracteres/s, y los nombres de componentes que faltan en el registro.
Catálogo de componentes
Todos los componentes reciben t, beats, dur, lang y vertical de la escena; las props que aparecen a continuación provienen de la línea de tiempo. Los números mostrados en pantalla usan la forma exacta del artículo: prefiere display / value como cadena sobre el texto calculado siempre que el artículo escriba "14/20", "0.0018" o "83 %".
| Componente | Uso | Props |
|---|---|---|
Counter | Una cifra principal que cuenta hacia arriba, con etiqueta debajo | value: number, label: Label, prefix?, suffix?, decimals? (0-8), beat? (índice del pulso en que comienza el conteo, por defecto 0) |
BarCompare | 2-5 barras horizontales que crecen una por pulso; la resaltada en el degradado de acento | bars: { label: Label; value: number; display?: string; highlight?: boolean; at?: number }[], max? (tope de escala, por defecto el valor más grande), unit? (se añade cuando no hay display) |
FlowDiagram | Cajas y flechas que aparecen en orden; una arista broken es discontinua y atenuada, con un espacio | nodes: { id; label: Label; at?: number }[], edges: { from; to; label?: Label; broken?: boolean }[], layout: 'vertical' | 'horizontal' (usa vertical para el corte 9:16) |
KeyQuote | La frase clave, palabra por palabra, palabras enfatizadas en el acento con subrayado | text: Label, emphasis?: string[] (palabras o frases, coinciden ignorando mayúsculas y puntuación), beat? |
StatGrid | 2-4 mosaicos de cifras que aparecen uno por pulso | stats: { value: string; label: Label; at?: number }[] |
at en una barra, nodo o mosaico lo fija a un segundo de la escena en lugar de su propio pulso. Los pasos sin un pulso o at siguen 0.6 s después del anterior. Cada componente está ajustado para que termine al final de la escena: el último fotograma siempre muestra el estado final.
Para ver la biblioteca completa: npm run studio y abre Gallery-vertical, o renderiza fotogramas de ella.
Agregar un componente personalizado
Cuando el guión marque una escena como "bespoke" (personalizada):
- Estado primero, con una prueba. Escribe
src/anim/<Nombre>.state.tscon el tipo de props y unframeState(props, t, beats, dur)puro que devuelva números y cadenas simples. Sin importaciones de Remotion ni React allí. Constrúyelo sobresrc/anim/steps.ts(sceneTime,stepTimes,fit,progress,beatAt,easeOutCubic) para que sea total: valores finitos en cualquiert, campos de progreso dentro de [0, 1], nada visible antes de su primer pulso, el estado final ent = dur. - Agrega un caso a
CASESentest/anim.test.ts(y una prueba de su propio comportamiento), luego ejecutanpm test. Verifica el estado ent < 0, cada pulso,dury más allá dedur, con pulsos faltantes, tardíos y muy cortos. - Dibújalo en
src/anim/<Nombre>.tsx: llama aframeState, dibuja dentro de<Stage vertical={...}>, usa los tokens de marca desrc/brand.ts(C,GRADIENT,alpha) e Inter. Sin nuevos colores ni fuentes. - Regístralo en
ANIMSensrc/anim/index.ts. ManténANIMScomo un objeto literal simple de nombres:npm run lintlos lee del código fuente. - Opcionalmente agrega una muestra a
src/Gallery.tsx. Si es reutilizable, pertenece de nuevo a la plantilla del plugin.
Imágenes generadas. La mayoría de las escenas deben dibujarse en código (SVG y CSS a partir de los tokens de marca). Si una escena realmente necesita una imagen generada (un fondo, una imagen base, un momento texturizado), un modelo de imagen puede crearla, por ejemplo la generación de imágenes de Codex CLI. Guárdala en public/, cárgala con staticFile(), mantenla dentro de la paleta de marca y verifica el fotograma.
Área segura vertical (1080x1920)
- La interfaz de la plataforma cubre aproximadamente los ~220 px superiores y los ~380 px inferiores; mantén 72 px en los lados.
- Las animaciones se dibujan solo dentro del escenario: desde y = 280 (debajo de la insignia de AGILabs) hasta y = 1280 (encima de las dos filas de subtítulos), 936 px de ancho.
Stage/stageOf()te dan ese recuadro. - Los subtítulos se colocan debajo del escenario, como máximo dos filas de ~32 caracteres, dentro de ~900 px.
Fotogramas: míralos
Renderiza fotogramas y Lee cada PNG antes de renderizar el video. Una compilación o verificación de lint limpia no muestra desbordamiento, texto recortado o un número incorrecto.
npx remotion still src/index.ts Main-EN-vertical out/stills/f0150.png --frame=150
Toma un fotograma por escena una vez que su último paso se haya reproducido, más uno cada ~3 s (cada 90 fotogramas a 30 fps). Verifica: insignia de AGILabs y nada más como marca, nada fuera del área segura, ningún texto desbordando su recuadro, cada cifra igual al artículo, y el subtítulo en pantalla coincidiendo con lo que la animación muestra en ese momento.