MÓDULO 4.3

📈 Data-Story

Dados sozinhos são chatos. Dados contados como história são memoráveis. Este módulo monta um vídeo que guia o espectador por uma narrativa de números: gráficos animados, contadores, comparações e texto em destaque — orquestrados com Series e transições cross-dissolve.

6
Tópicos
45
Minutos
Avançado
Nível
Projeto
Tipo
dados JSON/props chart-animation donut-chart stat-counter comparison-chart Data-Story narrativa coerente com dados
1

📖 Estrutura narrativa — o arco do dado

Um Data-Story eficaz não começa com o gráfico — começa com o problema. O espectador precisa entender o que está em jogo antes de ver o número. A sequência de cenas cria esse arco narrativo automaticamente quando você ordena bem o Series.

📈 O arco de 5 cenas

1.Contexto — uma frase que define o problema. Ex: "Em 2025, 68% dos vídeos foram assistidos sem som."
2.Dado central — o número mais importante, animado com stat-counter grande.
3.Proporção — donut chart mostrando como esse número se divide.
4.Tendência — line-chart mostrando evolução no tempo.
5.Conclusão — texto em destaque com a mensagem central + chamada para ação.

💡 Regra dos 5–8 segundos por cena

Cada cena de dados deve ter entre 5 e 8 segundos. Menos que 5 s: o espectador não processa o dado. Mais que 8 s: o ritmo cai e parece apresentação de PowerPoint. As transições cross-dissolve de 10–15 frames cobrem a mudança sem interromper o fluxo.

5 cenas
Arco completo
5–8 s
Por cena de dado
Contexto
Sempre primeiro
Total
~30–45 s
2

📊 Gráfico de barras animado — chart-animation

O chart-animation.tsx usa uma array de valores e anima cada barra crescendo de 0 até sua altura proporcional. O stagger entre barras faz o gráfico parecer estar "se preenchendo" — o olho do espectador acompanha a barra maior, que é normalmente o dado mais importante.

Barras com stagger chart-animation.tsx
const data = [42, 68, 55, 91, 37]; // valores %
const maxH = 200;        // px de altura máxima

{data.map((val, i) => {
  const delay = i * 6;  // stagger de 6 frames
  const barHeight = interpolate(
    frame, [delay, delay + 45],
    [0, (val / 100) * maxH],
    { extrapolateRight: "clamp", easing: Easing.out(Easing.quad) }
  );
  // rótulo aparece quando barra chega ao topo
  const labelOpacity = interpolate(
    frame, [delay + 40, delay + 55], [0, 1],
    { extrapolateRight: "clamp" }
  );
  return <div key={i} style={{ height: `${barHeight}px`, opacity: 1 }}>
    <span style={{ opacity: labelOpacity }}>{val}%</span>
  </div>;
})}

✓ Barras que funcionam

  • Stagger de 4–8 frames entre barras — perceptível sem ser arrastado.
  • Rótulo aparece quando a barra termina — 15 frames depois do delay da barra.
  • Easing.out para crescimento — começa rápido e desacelera ao chegar ao topo.

✗ Erros de gráfico

  • Todas as barras crescendo ao mesmo tempo — perde o efeito de revelação.
  • Rótulo aparece antes da barra terminar — parece um bug.
  • Easing.in para crescimento — começa lento demais, parece travado.
Stagger
delay = i * 6
Altura
(val/100) * maxH
Easing
out(quad)
Rótulo
+15f após barra
3

🍩 Donut chart de proporção

O donut-chart.tsx usa SVG com stroke-dasharray e stroke-dashoffset animados. Cada fatia é um <circle> com comprimento de arco calculado a partir da porcentagem. O reveal progressivo segue a mesma lógica das barras: Easing.out, revelação de cada fatia com pequeno stagger.

1

Calcular circunferência e offsets

const c = 2 * Math.PI * r — circunferência do círculo. Para uma fatia de 42%, o comprimento de arco é c * 0.42. O strokeDasharray é "arco restante" e o offset é rotacionado para posicionar corretamente.

2

Animação do strokeDashoffset

Começa com dashoffset = c (fatia invisível) e anima até dashoffset = c - arco (fatia completa). O resultado é a fatia "desenhando" no sentido horário.

3

Legenda aparece após reveal

Cada cor do donut tem uma legenda que entra com fade-in quando a fatia correspondente termina de aparecer. Legenda e fatia ficam visualmente associadas.

strokeDasharray
arco + resto
strokeDashoffset
c → c-arco
Rotação
-90° para iniciar no topo
Legenda
fade após reveal
4

🔢 Stat-counter e text-highlight combinados

A cena de dado central combina dois templates: stat-counter.tsx com o número animando de 0 até o valor, e text-highlight.tsx que destaca uma frase contextual com background colorido crescendo de 0% até 100% de width. Os dois entram em stagger — primeiro o número, depois o texto.

Counter + highlight em sequência DataScene.tsx
const frame = useCurrentFrame();

// número conta de 0 → 68% em 60 frames
const pct = Math.round(interpolate(
  frame, [0, 60], [0, 68],
  { easing: Easing.out(Easing.cubic), extrapolateRight: "clamp" }
));

// highlight cresce de 0 → 100% a partir do frame 50
const hlWidth = interpolate(
  frame, [50, 80], [0, 100],
  { extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);

return <AbsoluteFill style={{ display:"flex", flexDirection:"column", alignItems:"center", justifyContent:"center" }}>
  <span style={{ fontSize: 120, fontWeight: 800, color: "#fbbf24" }}>{pct}%</span>
  <div style={{ position: "relative" }}>
    <div style={{ position:"absolute", inset:0, background:"rgba(251,191,36,0.25)", width:`${hlWidth}%` }}/>
    <span>dos vídeos são assistidos sem som</span>
  </div>
</AbsoluteFill>;
Counter
0 → valor em 60f
Highlight
width 0 → 100%
Stagger
Counter primeiro
Fonte
120px+ para impacto
5

⚔️ Gráfico de comparação — comparison-chart

O comparison-chart.tsx mostra duas barras lado a lado que crescem de forma diferente — o argumento visual mais poderoso para mostrar diferença. A regra de ouro: a barra "ruim" aparece primeiro, cresce um pouco, e então a barra "boa" ultrapassa ela dramaticamente.

✓ Comparação impactante

  • Barra negativa (vermelha) aparece primeiro — âncora baixa.
  • Barra positiva (âmbar/verde) cresce depois e ultrapassa — drama visual.
  • Rótulo de diferença percentual aparece com spring no topo da barra maior.

✗ Comparação fraca

  • Ambas as barras crescendo juntas — perde o efeito de "ultrapassagem".
  • Cores parecidas para os dois lados — comparação não fica evidente.
  • Sem rótulo de diferença — o espectador precisa calcular mentalmente.
Ordem
Ruim → bom
Delay
+15f para a 2ª barra
Cores
Vermelho vs âmbar
Δ%
Spring no topo
6

🔀 Transições cross-dissolve entre cenas

O cross-dissolve é a transição ideal para Data-Story: fluida o suficiente para não quebrar o raciocínio, mas marcando claramente a mudança de cena. Implementa-se sobrepondo dois Sequence com overlap e animando as opacidades em sentidos opostos nos frames de sobreposição.

Cross-dissolve manual com overlap DataStory.tsx
// Cena A: frames 0–90 | Cena B: frames 78–168 | overlap: 78–90
const DISSOLVE = 12; // frames de sobreposição

return (
  <AbsoluteFill>
    <Sequence from={0} durationInFrames={90}>
      <div style={{ opacity: interpolate(frame,
        [90 - DISSOLVE, 90], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }) }}>
        <CenaA />
      </div>
    </Sequence>
    <Sequence from={90 - DISSOLVE}>
      <div style={{ opacity: interpolate(frame,
        [90 - DISSOLVE, 90], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }) }}>
        <CenaB />
      </div>
    </Sequence>
  </AbsoluteFill>
);

💡 10–15 frames de dissolve

Dissolve de 10 frames (0.33 s a 30fps) é quase imperceptível mas suaviza o corte. Dissolve de 15 frames (0.5 s) é notável e mais elegante. Acima de 20 frames começa a parecer propositalmente dramático — só use se quiser esse efeito.

Overlap
10–15 frames
Cena A
opacity 1 → 0
Cena B
opacity 0 → 1
extrapolate
clamp em ambos

🏁 Resumo do Módulo

Arco de 5 cenas — contexto → dado → proporção → tendência → conclusão.
Barras com stagger — delay = i * 6, rótulo +15f após barra, Easing.out(quad).
Donut via SVG — strokeDashoffset animado de c até c-arco com rotação -90°.
Counter + highlight — número grande com width animada por baixo do texto contextual.
Comparação dramática — barra ruim primeiro, boa ultrapassa com delay de 15 frames.
Cross-dissolve — 12 frames de overlap, opacidades inversas entre cenas consecutivas.

Próximo Módulo:

4.4 — Reels / Shorts: vídeo vertical 9:16, typewriter, glitch, sound-wave, bokeh e subscribe reminder.