PTENES
MÓDULO 2.3

🎞️ Animación de contenido

9 plantillas para animar listas, cards, timers, notificaciones, partículas y sonidos. Cada una enseña un patrón diferente: stagger, CSS 3D, estado derivado del frame, físicas simples y loops perpetuos.

9
Plantillas
45
Minutos
Inter
Nivel
Composición
Tipo
Línea de tiempo: stagger = i × 10 frames F0 F10 F20 F30 F40 ✅ Elemento 1 — from=0 ✅ Elemento 2 — from=10 ✅ Elemento 3 — from=20 ✅ Elemento 4 — from=30
1

🎞️ Vista general — patrones de composición temporal

La categoría "Animación de contenido" agrupa plantillas que van más allá de animar un solo elemento — ellas orquestan múltiples elementos en el tiempo. El patrón central es el stagger mediante Sequence con from escalado, pero la categoría también abarca 3D CSS, simulación física y loops perpetuos.

🎯 Los 4 patrones de esta categoría

1. Stagger secuencial

animated-list, notification-pop, progress-steps — varios elementos con from escalado.

2. CSS 3D

card-flip, rotating-carousel — perspective + rotateY para dar profundidad.

3. Estado derivado del frame

countdown-timer — el número mostrado se calcula directamente del frame, sin useState.

4. Simulación física

particle-explosion, sound-wave — posición/altura calculada con aritmética por frame.

9 plantillas
Categoría
4 patrones
Stagger/3D/Derivado/Física
Sequence
Herramienta central
Sin useState
Todo viene del frame
2

📋 animated-list — stagger de elementos

O animated-list.tsx es la plantilla de referencia para stagger de varios elementos. Cada elemento de una lista aparece con un retraso proporcional a su índice, creando esa sensación de «los elementos van llegando», omnipresente en los videos explicativos y las diapositivas animadas.

animated-list — stagger con Sequence templates/animated-list.tsx
const DELAY_PER_ITEM = 10; // frames entre cada item

{items.map((item, i) => (
  <Sequence key={i} from={i * DELAY_PER_ITEM}>
    <ListItem item={item} />
  </Sequence>
))}

// Dentro de ListItem: frame é LOCAL (começa em 0)
const frame = useCurrentFrame(); // 0, 1, 2...
const opacity = interpolate(frame, [0, 15], [0, 1],
  { extrapolateRight: "clamp" });
const y = interpolate(frame, [0, 15], [20, 0],
  { extrapolateRight: "clamp" });

📊 El poder del frame LOCAL dentro de Sequence

Cuando usas <Sequence from={i * 10}>, el componente hijo recibe useCurrentFrame() comenzando en 0 cuando inicia la secuencia. Esto significa que cada ListItem puede tener la misma lógica de animación — no necesitas restar el offset manualmente. Remotion se encarga del desplazamiento.

✓ Correcto: Sequence + frame local

<Sequence from={i * 10}>
  <Item />  // frame começa em 0
</Sequence>

Cada Item tiene la misma lógica: limpio y reutilizable.

✗ Incorrecto: restar el offset manualmente

const localFrame = frame - i * 10;
if (localFrame < 0) return null;

Verboso, propenso a errores de borde (frame negativo).

Patrón
Secuencia from=i×delay
Frame local
Empieza en 0 en el hijo
Animación
fade + translateY
Reutiliza en
viñetas, notificaciones, pasos
3

🃏 card-flip — CSS 3D en Remotion

O card-flip.tsx demuestra cómo implementar CSS 3D dentro de Remotion: dos caras de una tarjeta posicionadas con backfaceVisibility: "hidden", el contenedor con perspective, y la rotación interpolada de 0° a 180°.

card-flip — rotación 3D CSS templates/card-flip.tsx
const frame = useCurrentFrame();

const rotation = interpolate(
  frame, [15, 45], [0, 180],
  { extrapolateRight: "clamp" }
);

return (
  {/* Contêiner: define a perspectiva */}
  <div style={{ perspective: 800, transformStyle: "preserve-3d" }}>
    {/* Frente */}
    <div style={{
      backfaceVisibility: "hidden",
      transform: `rotateY(${rotation}deg)`,
      position: "absolute", inset: 0
    }}>Face A</div>
    {/* Verso — começa já virado 180° */}
    <div style={{
      backfaceVisibility: "hidden",
      transform: `rotateY(${rotation - 180}deg)`,
      position: "absolute", inset: 0
    }}>Face B</div>
  </div>
);

💡 ¿Por qué backfaceVisibility: "hidden"?

Sin esta propiedad, verías ambas caras al mismo tiempo: el frente aparecería reflejado detrás y viceversa. Con hidden, cada cara solo es visible cuando está "de frente" al observador. Es el secreto de cualquier flip card.

CSS 3D
perspective + preserve-3d
Faces
backfaceVisibility: hidden
Rotación
0° → 180°
Reverso
rotation - 180°
4

⏳ countdown-timer — estado derivado del frame

O countdown-timer.tsx ilustra un concepto central de Remotion: el valor mostrado siempre es una función directa del frame, sin useState ni efectos. El número mostrado se calcula a partir del frame actual y de los parámetros de duración/fps.

countdown-timer — derivación del frame templates/countdown-timer.tsx
const frame = useCurrentFrame();
const { fps, durationInFrames } = useVideoConfig();

// segundos restantes — derivados do frame
const secondsLeft = Math.ceil(
  (durationInFrames - frame) / fps
);

// "GO" quando chega a 0
const label = secondsLeft > 0 ? String(secondsLeft) : "GO!";

// spring de entrada para cada mudança de número
const frameInSegment = frame % fps; // 0→fps a cada segundo
const scale = spring({ fps, frame: frameInSegment,
  config: { damping: 10, stiffness: 150 }
});

Secuencia de eventos en el countdown (30fps, 5s):

F0

Frame 0 — muestra «5»

(150 - 0) / 30 = 5 → Math.ceil(5) = 5. Spring entra con scale-pop.

F30

Frame 30 — cambia a «4»

(150 - 30) / 30 = 4 → el número cambia, spring se reinicia (frame % fps = 0).

F150

Frame 150 — muestra «GO!»

secondsLeft ≤ 0 → label = "GO!". Sin estado, sin setInterval.

Derivación
Math.ceil(remaining/fps)
Sin estado
Cero useState
Spring
frame % fps para pop
¡GO!
Condicional simple
5

💥 particle-explosion — física 2D determinista

O particle-explosion.tsx simula una explosión de partículas con trayectoria física: posición inicial en el centro, velocidad con ángulo aleatorio (sembrado) y gravedad simple. Todo se calcula frame a frame — sin estado acumulado.

particle-explosion — física con semilla templates/particle-explosion.tsx
const PARTICLE_COUNT = 40;

{Array.from({ length: PARTICLE_COUNT }, (_, i) => {
  // velocidade e ângulo DETERMINÍSTICOS
  const angle = random(`angle-${i}`) * 2 * Math.PI;
  const speed = random(`speed-${i}`) * 8 + 2;

  const vx = Math.cos(angle) * speed;
  const vy = Math.sin(angle) * speed;

  // posição frame-a-frame com gravidade
  const x = cx + vx * frame;
  const y = cy + vy * frame + 0.15 * frame * frame; // g

  const opacity = interpolate(frame, [20, 45], [1, 0],
    { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
})}

⚠️ Sin random() con seed = video corrupto

Si usas Math.random() aquí, cada frame calculado produce una trayectoria diferente — en el renderizado en paralelo de Remotion, las partículas «saltan» a posiciones aleatorias. El video final parece estar dañado. random("seed") garantiza que el frame 42 siempre tenga las mismas partículas en el mismo lugar.

random(seed)
Trayectoria determinista
Física
v × frame + ½g×frame²
Fade-out
interpolate fotogramas finales
Sin estado
Posición recalculada
6

🎵 sound-wave & text-highlight — loops y enfoque

Dos templates que muestran patrones complementarios: sound-wave.tsx crea un visualizador de audio (barras que oscilan con Math.sin) y text-highlight.tsx destaca una palabra a la vez en sincronía con el frame, como en el karaoke o la lectura guiada.

sound-wave

{bars.map((_, i) => {
  const h = Math.sin(
    frame / 8         // velocidade
    + i * 0.4       // fase
  ) * 0.5 + 0.5;   // 0 a 1
  return <rect height={h * maxH}/>;
})}

text-highlight

const words = text.split(" ");
const framesPerWord = fps / 2;
const activeIndex = Math.floor(
  frame / framesPerWord
);

{words.map((w, i) => (
  <span style={{
    color: i === activeIndex
      ? "#60a5fa" : "white"
  }}>{w}</span>
))}

💡 text-highlight: sincronizar con audio

El mismo patrón de Math.floor(frame / framesPerWord) se puede adaptar para sincronizar palabras con marcas de tiempo de la transcripción. En los módulos avanzados de T3, verás cómo combinar esta plantilla con @remotion/captions para karaoke automático.

sound-wave
Math.sin + fase
highlight
Math.floor(frame/rate)
Loop
Sin: periódico
Sincronía
fps/palabras/seg
7

📚 Biblioteca completa — Animación de contenido

PlantillaDescripciónPatrón central
animated-list.tsx Lista con stagger escalonado Secuencia from=i×delay
card-flip.tsx Card con flip 3D rotateY + backfaceVisibility
countdown-timer.tsx Cuenta regresiva 5-4-3-2-1-GO Math.ceil(remaining/fps)
notification-pop.tsx Toasts apilados con spring Sequence anidada + spring
particle-explosion.tsx Explosión con física 2D random(seed) + vx/vy×frame
progress-steps.tsx Pasos con indicador activo Math.floor(frame/stepDuration)
rotating-carousel.tsx Carrusel que rota en 3D rotateY por slot + loop
sound-wave.tsx Visualizador de barras de audio Math.sin + fase por barra
text-highlight.tsx Palabra activa por segundo Math.floor(frame/fps×rate)

📌 Resumen del módulo

✓
Sequence + frame local — patrón correcto de stagger; no restes el offset manualmente.
✓
CSS 3D = perspective + backfaceVisibility — card-flip y carrusel sin biblioteca 3D.
✓
Estado derivado del frame — countdown sin useState; todo se calcula con Math.ceil(remaining/fps).
✓
random(seed) para física — partículas reproducibles entre renders paralelos.
✓
Math.sin para loops perpetuos — sound-wave y cualquier oscilación continua sin principio ni fin.

Próximo módulo:

2.4 — Imagen y medios: galerías, sliders, comparadores y marcos para fotos en video.