🎭 Stage — props e features built-in
<Stage> é o container raiz de toda animação. Inclui autoescala (1920×1080 padrão), scrubber timeline, controles play/pause, e props que você pode customizar.
Props completas
| Prop | Tipo | Default | Para quê |
|---|---|---|---|
duration | number (ms) | obrigatório | Duração total da timeline |
autoPlay | boolean | true | Inicia tocando ao montar |
loop | boolean | false | Recomeça quando termina |
scrubber | boolean | true | Mostra timeline com scrub manual |
controls | boolean | true | Mostra play/pause/restart |
autoScale | boolean | true | Escala 1920×1080 para qualquer viewport |
canvasSize | {w, h} | {1920, 1080} | Override resolução interna |
Use case: video export-ready
// Para gerar vídeo exportável (sem UI overlays)
<Stage
duration={5000}
autoPlay={false}
scrubber={false}
controls={false}
loop={false}
>
{/* Sprites aqui — nenhum controle visível */}
{/* Use frame-by-frame screenshot externo para gerar MP4 */}
</Stage>
✨ Sprite — lifecycle, props, sub-sprites
Cada <Sprite> tem janela de existência (start..end ms). Antes do start: não montado. Após end: desmontado. Hidden não é "display: none" — é unmount real (children não rodam).
Composição de cenas — 3 padrões
// PADRÃO 1: Sequencial (um após outro)
<Sprite start={0} end={1000}>Hero</Sprite>
<Sprite start={1000} end={2000}>Cards</Sprite>
<Sprite start={2000} end={3000}>CTA</Sprite>
// PADRÃO 2: Paralelo (sobreposição total)
<Sprite start={0} end={2000}>Background pulse</Sprite>
<Sprite start={0} end={2000}>Foreground content</Sprite>
// Os dois rodam simultaneamente
// PADRÃO 3: Stagger (entrada escalonada)
{[0, 200, 400, 600].map(delay => (
<Sprite key={delay} start={delay} end={delay + 800}>
<Card index={delay/200} />
</Sprite>
))}
// 4 cards entram com 200ms de gap entre eles
Sub-sprites (Sprites aninhados)
// Sprite "pai" cria janela de tempo
<Sprite start={1000} end={4000}>
<HeroSection>
{/* Sprite "filho" começa relativo ao pai */}
<Sprite start={500} end={1500}>
<Headline /> {/* aparece em t=1500ms (pai 1000 + filho 500) */}
</Sprite>
<Sprite start={800} end={2000}>
<Subhead />
</Sprite>
</HeroSection>
</Sprite>
Sprites aninhados herdam contexto do pai mas têm timeline própria. Útil para cenas complexas com múltiplos elementos.
⏱️ useTime vs useSprite — qual escolher
useTime() — tempo absoluto
const t = useTime();
// t = ms desde o início do Stage
// 0..duration
// Use para:
// - Animações que dependem de tempo absoluto
// - Sincronia com música/áudio
// - Loops contínuos
// - Pulsing/breathing (Math.sin(t/500))
useSprite() — progresso 0-1
const p = useSprite();
// p = progresso do Sprite atual
// 0 no start, 1 no end
// Use para:
// - Animações de entrada/saída
// - Reutilizar componente em Sprites de durações diferentes
// - Casos onde o tempo absoluto não importa
Exemplo combinado: hero com pulse contínuo
function Hero() {
const t = useTime(); // tempo absoluto (para pulse)
const p = useSprite(); // progresso da entrada
const pulse = 1 + Math.sin(t / 400) * 0.05; // breathing
const entry = Easing.easeOut(p); // entrada suave
return (
<h1 style={{
transform: `scale(${entry * pulse})`,
opacity: entry
}}>
Welcome
</h1>
);
}
<Sprite start={0} end={1500}>
<Hero />
</Sprite>
📈 Easing — todas as curvas + quando usar cada
Catálogo de easings
| Easing | Comportamento | Use case |
|---|---|---|
linear | Velocidade constante | Loops, scrolls automáticos |
easeIn | Lento → rápido | Saídas (algo "se afasta") |
easeOut | Rápido → lento | Entradas (default 80% dos casos) |
easeInOut | Lento → rápido → lento | Transições entre estados |
easeBack | Volta levemente antes de chegar | Drama, emphasis (CTA) |
easeBounce | Quica ao chegar | Playful, casual (não premium) |
interpolate — mapeamento avançado
// Forma básica: 0-1 → range customizado
const y = interpolate(p, [0, 1], [40, 0]);
// p=0 → y=40, p=0.5 → y=20, p=1 → y=0
// Multi-stop: anima entre 3 pontos
const opacity = interpolate(
p,
[0, 0.5, 1], // entrada (0..0.5) e saída (0.5..1)
[0, 1, 0]
);
// p=0 → 0, p=0.25 → 0.5, p=0.5 → 1, p=0.75 → 0.5, p=1 → 0
// Combinado com easing
const x = interpolate(
Easing.easeOut(p),
[0, 1],
[-100, 0]
);
⚡ Entry/exit primitives + performance
Helpers prontos (cobrem 80% dos casos)
import { fadeIn, fadeOut, slideInUp, slideOutDown,
scaleIn, scaleOut, slideInLeft, slideInRight } from 'animations';
// Uso direto:
<Sprite start={0} end={800}>
<div style={fadeIn()}>
Conteúdo aparece em fade
</div>
</Sprite>
// Combinado:
<Sprite start={0} end={800}>
<div style={{ ...fadeIn(), ...slideInUp({ distance: 40 }) }}>
Fade + slide combinados
</div>
</Sprite>
Performance: o que acelera GPU
- ✓transform (translateX, translateY, scale, rotate) — GPU accelerated
- ✓opacity — GPU accelerated
- ✓filter (blur, brightness) — GPU accelerated mas mais caro
- ✗width, height, top, left, padding — causa reflow, lento
- ✗background-color animado — repaint constante, evite
🎁 Popmotion fallback + consumo de quota
Citação literal — quando cair em Popmotion
"Só use Popmotion (https://unpkg.com/popmotion@11.0.5/dist/popmotion.min.js) se o componente inicial realmente não cobrir o caso de uso."
Casos que justificam Popmotion
- •Spring physics realista (animations.jsx tem easing, mas não simulação de mola completa)
- •Drag/gesture handling com inércia
- •Path animation ao longo de SVG path
- •Color interpolation avançada (HSL, OKLCH path)
CDN exata: https://unpkg.com/popmotion@11.0.5/dist/popmotion.min.js (versão pinned).
⚠️ Animações consomem quota agressivamente
Geração de uma animação complexa pode consumir 5-10x mais tokens que uma landing equivalente. Razões:
- • Múltiplos Sprites = mais código por geração
- • Iteração de timing precisa de feedback visual constante
- • Verificador roda screenshots em múltiplos frames
No plano Pro: 2-3 sessões de animação intensa podem esgotar cota semanal. Para uso rotineiro, Max ($100-200/mês) é necessário.
Hack: itere com prompt simples + Tweaks
"Create initial animation: hero fade-in 0-800ms,
3 cards stagger 600-1800ms, CTA pulse 2000-3000ms.
Total Stage duration 3500ms.
Expose Tweaks for:
- duration multiplier (0.5x, 1x, 1.5x, 2x)
- stagger gap (100ms, 200ms, 300ms)
- CTA easing (easeOut, easeBack, easeBounce)
I want to iterate on timing without re-prompting."
Tweaks > re-prompt para ajustar timing — economiza tokens dramaticamente em animações.
✅ Resumo do Módulo
Próximo Módulo:
3.6 — 🎯 Edge cases + GitHub flow + URLs internas + .napkin