MÓDULO 2.3

🎞️ Animação de Conteúdo

9 templates para animar listas, cards, timers, notificações, partículas e sons. Cada um ensina um padrão diferente: stagger, 3D CSS, estado derivado do frame, físicas simples e loops perpétuos.

9
Templates
45
Minutos
Inter
Nível
Composição
Tipo
Timeline: stagger = i × 10 frames F0 F10 F20 F30 F40 ✅ Item 1 — from=0 ✅ Item 2 — from=10 ✅ Item 3 — from=20 ✅ Item 4 — from=30
1

🎞️ Visão geral — Padrões de composição temporal

A categoria "Animação de Conteúdo" agrupa templates que vão além de animar um único elemento — eles orquestram múltiplos elementos no tempo. O padrão central é o stagger via Sequence com from escalado, mas a categoria também cobre 3D CSS, simulação física e loops perpétuos.

🎯 Os 4 padrões desta categoria

1. Stagger sequencial

animated-list, notification-pop, progress-steps — vários elementos com from escalado.

2. 3D CSS

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

3. Estado derivado do frame

countdown-timer — o número exibido é calculado diretamente do frame, sem useState.

4. Simulação física

particle-explosion, sound-wave — posição/altura calculada com aritmética por frame.

9 templates
Categoria
4 padrões
Stagger/3D/Derivado/Física
Sequence
Ferramenta central
Sem useState
Tudo vem do frame
2

📋 animated-list — stagger de itens

O animated-list.tsx é o template de referência para stagger de múltiplos elementos. Cada item de uma lista entra com um atraso proporcional ao índice, criando aquela sensação de "os itens vão chegando" que é onipresente em explainer videos e slides animados.

animated-list — stagger com 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" });

📊 O poder do frame LOCAL dentro da Sequence

Quando você usa <Sequence from={i * 10}>, o componente filho recebe useCurrentFrame() começando em 0 quando a sequência inicia. Isso significa que cada ListItem pode ter a mesma lógica de animação — não precisa subtrair o offset manualmente. O Remotion cuida do deslocamento.

✓ Certo: Sequence + frame local

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

Cada Item tem lógica igual — limpo e reutilizável.

✗ Errado: subtrair offset manualmente

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

Verboso, propenso a bugs de borda (frame negativo).

Padrão
Sequence from=i×delay
Frame local
Começa em 0 no filho
Animação
fade + translateY
Reutiliza em
bullets, toasts, steps
3

🃏 card-flip — CSS 3D no Remotion

O card-flip.tsx demonstra como implementar 3D CSS dentro do Remotion: duas faces de um card posicionadas com backfaceVisibility: "hidden", o contêiner com perspective, e a rotação interpolada de 0° a 180°.

card-flip — rotação 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 que backfaceVisibility: "hidden"?

Sem essa propriedade, você veria as duas faces ao mesmo tempo — a frente aparecer espelhada atrás e vice-versa. Com hidden, cada face só é visível quando está "de frente" para o observador. É o segredo de qualquer flip card.

CSS 3D
perspective + preserve-3d
Faces
backfaceVisibility: hidden
Rotação
0° → 180°
Verso
rotation - 180°
4

⏳ countdown-timer — estado derivado do frame

O countdown-timer.tsx ilustra um conceito central do Remotion: o valor exibido é sempre uma função direta do frame, sem useState, sem efeitos. O número exibido é calculado a partir do frame atual e dos parâmetros de duração/fps.

countdown-timer — derivação do 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 }
});

Sequência de eventos no countdown (30fps, 5s):

F0

Frame 0 — exibe "5"

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

F30

Frame 30 — troca para "4"

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

F150

Frame 150 — exibe "GO!"

secondsLeft ≤ 0 → label = "GO!". Sem estado, sem setInterval.

Derivação
Math.ceil(remaining/fps)
Sem estado
Zero useState
Spring
frame % fps para pop
GO!
Condicional simples
5

💥 particle-explosion — física 2D determinística

O particle-explosion.tsx simula uma explosão de partículas com trajetória física: posição inicial no centro, velocidade com ângulo aleatório (semeado) e gravidade simples. Tudo calculado frame a frame — sem estado acumulado.

particle-explosion — física semeada 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" });
})}

⚠️ Sem random() semeado = vídeo corrompido

Se você usar Math.random() aqui, cada frame calculado produz uma trajetória diferente — no render paralelo do Remotion, as partículas "pulam" para posições aleatórias. O vídeo final parece corrompido. random("seed") garante que frame 42 sempre tem as mesmas partículas no mesmo lugar.

random(seed)
Trajetória determinística
Física
v × frame + ½g×frame²
Fade-out
interpolate frames finais
Sem estado
Posição recalculada
6

🎵 sound-wave & text-highlight — loops e foco

Dois templates que demonstram padrões complementares: sound-wave.tsx cria um visualizador de áudio (barras que oscilam com Math.sin) e text-highlight.tsx destaca uma palavra por vez em sincro com o frame — como karaokê ou leitura 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 com áudio

O mesmo padrão de Math.floor(frame / framesPerWord) pode ser adaptado para sincronizar palavras com timestamps de transcrição. Nos módulos avançados da T3, você verá como combinar esse template com @remotion/captions para karaokê automático.

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

📚 Biblioteca Completa — Animação de Conteúdo

TemplateDescriçãoPadrão central
animated-list.tsx Lista com stagger escalonado Sequence from=i×delay
card-flip.tsx Card com flip 3D rotateY + backfaceVisibility
countdown-timer.tsx Contagem 5-4-3-2-1-GO Math.ceil(remaining/fps)
notification-pop.tsx Toasts empilhados com spring Sequence aninhada + spring
particle-explosion.tsx Explosão com física 2D random(seed) + vx/vy×frame
progress-steps.tsx Passos com indicador ativo Math.floor(frame/stepDuration)
rotating-carousel.tsx Carrossel 3D rotacionando rotateY por slot + loop
sound-wave.tsx Visualizador de barras de áudio Math.sin + fase por barra
text-highlight.tsx Palavra ativa por segundo Math.floor(frame/fps×rate)

📌 Resumo do módulo

Sequence + frame local — padrão correto de stagger; não subtraia offset manualmente.
3D CSS = perspective + backfaceVisibility — card-flip e carrossel sem biblioteca 3D.
Estado derivado do frame — countdown sem useState; tudo calculado de Math.ceil(remaining/fps).
random(seed) para física — partículas reproduzíveis entre renders paralelos.
Math.sin para loops perpétuos — sound-wave e qualquer oscilação contínua sem começo/fim.

Próximo módulo:

2.4 — Imagem & Mídia: galerias, sliders, comparadores e molduras para fotos em vídeo.