name: product-demo-video description: Esta skill deve ser usada quando o usuário pedir para "fazer um vídeo de demonstração de produto", "criar um vídeo de demonstração de aplicativo", "criar um vídeo de anúncio de funcionalidade", "fazer uma demo de SaaS", "fazer um vídeo de walkthrough de interface", "animar capturas de tela de produto em uma demonstração", "adicionar um cursor/clique animado a uma captura de tela" ou "ampliar parte de uma interface". Abrange a transformação de capturas de tela/designs estáticos (não gravações de tela) em uma demonstração animada limpa: molduras de dispositivo/navegador, cursor animado + ondulação de clique, zoom/pan para interface focal, anotações de destaque, ritmo de etapas, transições entre telas e legendas de funcionalidades. version: 0.1.0
Vídeo de Demonstração de Produto
Transforme capturas de tela ou designs de produto estáticos em uma demonstração animada e polida — sem gravador de tela, sem cursor real trêmulo, sem desvio de versão da interface. Emoldure cada tela, deslize um cursor sintético para o pixel exato, faça a ondulação do clique, então amplie a parte que importa e legende a funcionalidade. Como a entrada é estática e o movimento é código, cada valor é determinístico e é renderizado novamente de forma idêntica quando a captura de tela muda.
Quando usar
- Demonstração de aplicativo / SaaS, anúncio de funcionalidade, walkthrough de onboarding (15–90s).
- Mostrar um caminho de cliques através de uma interface da qual você só tem capturas de tela/designs.
- Destacar uma região focal de um painel denso (zoom para região + anotação).
Por que capturas de tela são melhores que gravações de tela
| Gravação de tela | Baseada em captura de tela (esta skill) |
|---|---|
| O cursor real se desvia, ultrapassa, treme | O cursor segue um caminho suave até um alvo exato |
| Regravar toda a tomada para corrigir uma etapa | Troque um PNG, renderize novamente o mesmo código |
| A interface muda durante a gravação, o mouse procura | Cada tela é um ativo limpo e de estado final |
| Difícil ampliar sem pixelização | Exporte capturas de tela em 2× (DPR 2) → zoons nítidos |
Capture as entradas com o dobro do tamanho da tela (retina / deviceScaleFactor: 2) para que um zoom de 2× permaneça nítido. Consulte references/screenshot-demo.md para um script de captura do Playwright.
O loop da demonstração
Cada demonstração é a mesma batida, repetida por funcionalidade. Segure o tempo suficiente para ler, mova-se com intenção, concretize o clique, revele o resultado.
| Batida | Duração | Função |
|---|---|---|
| Pousar na tela | 0.5–1s | Deixe o espectador se orientar antes que algo se mova |
| O cursor viaja até o alvo | 0.4–0.8s | Movimento suave (ease-in-out), não linear |
| Ondulação do clique + mudança de estado | 0.2–0.3s | Feedback tátil; a tela reage no mesmo quadro |
| Zoom para região focal | 1–2s para ampliar, segurar 2–4s | 2× é o ponto ideal — detalhes suficientes, mantém o contexto |
| Legendar a funcionalidade | segurar com o zoom | Uma frase de benefício, ex. "Exportação com um clique" |
Mova a câmera ou o cursor, raramente ambos ao mesmo tempo — dois movimentos simultâneos dividem a atenção.
Emoldurar a captura de tela
Uma captura de tela bruta parece um relatório de bug; uma emoldurada parece um produto. Envolva cada tela em um navegador ou moldura de dispositivo em um fundo com a marca.
// Moldura de navegador — coloque uma captura de tela, obtenha uma janela credível
const BrowserFrame = ({ url, children }) => (
<div style={{ borderRadius: 12, overflow: "hidden", background: "#1e1e22",
boxShadow: "0 40px 80px rgba(0,0,0,.35)" }}>
<div style={{ height: 40, display: "flex", alignItems: "center", gap: 8, padding: "0 14px" }}>
{["#ff5f57", "#febc2e", "#28c840"].map((c) => (
<span key={c} style={{ width: 12, height: 12, borderRadius: "50%", background: c }} />
))}
<div style={{ marginLeft: 12, flex: 1, height: 24, borderRadius: 6, background: "#2b2b30",
color: "#9aa", fontSize: 13, display: "flex", alignItems: "center", padding: "0 10px" }}>
{url}
</div>
</div>
<div style={{ background: "#fff" }}>{children}</div>
</div>
);
Coloque uma URL real na barra de endereços (autenticidade), adicione uma sombra suave e um fundo gradiente. Para dispositivos móveis, troque para uma moldura de telefone com um entalhe e cantos arredondados. Mantenha o estilo da moldura em um objeto de tema para que todas as telas correspondam.
Cursor animado + ondulação de clique (o movimento principal)
Direcione a posição a partir do quadro atual, nunca de uma transição CSS ou relógio de parede — um renderizador de vídeo precisa que cada quadro seja uma função pura do número do quadro, ou o cursor dessincroniza e pisca.
import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion";
// Viagem suave de `from` para `to` entre startFrame e startFrame+dur
const useCursor = (from, to, startFrame, dur) => {
const frame = useCurrentFrame();
const t = interpolate(frame, [startFrame, startFrame + dur], [0, 1],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp", easing: (x) => x * x * (3 - 2 * x) }); // smoothstep
return { x: from.x + (to.x - from.x) * t, y: from.y + (to.y - from.y) * t };
};
export const Cursor = ({ from, to, startFrame, dur, clickAt }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const { x, y } = useCursor(from, to, startFrame, dur);
// A ondulação se expande uma vez, no quadro do clique
const since = frame - clickAt;
const showRipple = since >= 0 && since < 18;
const r = spring({ frame: since, fps, config: { damping: 18 }, durationInFrames: 18 });
return (
<>
{showRipple && (
<div style={{ position: "absolute", left: x, top: y, transform: "translate(-50%,-50%)",
width: 12 + r * 60, height: 12 + r * 60, borderRadius: "50%",
border: "2px solid rgba(80,140,255,.9)", opacity: 1 - r }} />
)}
<svg width="28" height="28" viewBox="0 0 28 28"
style={{ position: "absolute", left: x, top: y, filter: "drop-shadow(0 2px 3px rgba(0,0,0,.4))" }}>
<path d="M3 2 L3 21 L9 15 L13 24 L16 23 L12 14 L20 14 Z" fill="#fff" stroke="#222" strokeWidth="1.4" />
</svg>
</>
);
};
O cursor usa smoothstep para acelerar a partir do repouso e desacelerar até o alvo (um cursor linear parece robótico). Dispare a ondulação e a mudança de estado da tela no mesmo quadro clickAt para que causa e efeito se alinhem. Consulte references/screenshot-demo.md para a composição completa com vários cliques em várias telas.
Ampliar para uma região (Ken Burns para interface)
Para ampliar um elemento focal, dimensione a tela e defina transform-origin para o centro desse elemento, para que o zoom convirja para ele em vez do centro do quadro.
const ZoomToRegion = ({ target, scale = 2, startFrame, dur, children }) => {
const frame = useCurrentFrame();
const s = interpolate(frame, [startFrame, startFrame + dur], [1, scale],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp", easing: (x) => 1 - Math.pow(1 - x, 3) }); // easeOutCubic
return (
<div style={{ transform: `scale(${s})`, transformOrigin: `${target.x}px ${target.y}px`,
willChange: "transform" }}>
{children}
</div>
);
};
Limite o zoom a cerca de 2× (acima de 3× perde o contexto e desorienta). Segure o estado ampliado por 2–4s — tempo suficiente para ler o detalhe — então volte suavemente antes de passar para a próxima tela. Combine o zoom com uma anotação, nunca cru.
Anotações de destaque e legendas de funcionalidades
Anote o ponto de inflexão e deixe-o desaparecer. Uma anotação é um rótulo + um conector (seta, círculo ou sublinhado) que aponta para o elemento focal.
- Destaque o elemento com um anel suave ou entorno escurecido (
box-shadow: 0 0 0 9999px rgba(0,0,0,.45)corta um buraco de holofote). - Legenda um benefício por batida — frase nominal curta ("Análise em tempo real"), não uma frase inteira. Apareça no zoom, desapareça antes do próximo passo.
- Mantenha as legendas no terço inferior e dentro da área segura para título para que sobrevivam a cortes para 9:16.
Consulte references/annotation-and-transitions.md para máscaras de holofote, conectores de seta e uma trilha de legendas digitadas.
Transições entre telas
Entre as etapas, mova as telas com uma linguagem consistente. Escolha uma e mantenha-a:
| Transição | Sensação | Uso |
|---|---|---|
| Empurrar / deslizar | movimento "próximo passo" para frente | Walkthroughs lineares |
| Zoom cruzado | momentum, como aplicativo | Saltar para uma visualização de detalhes |
| Crossfade | calmo, neutro | Mudança de contexto (configurações → painel) |
| Corte correspondente em um elemento compartilhado | perfeito | O mesmo cartão persiste entre telas |
Segure a tela de saída até que seu clique se resolva, então transicione — nunca corte no meio do movimento. Consulte references/annotation-and-transitions.md.
Lista de verificação de saída
- Entradas capturadas em 2× (DPR 2); zooms permanecem nítidos em 2×.
- Cada valor de cursor/zoom é uma função pura de
useCurrentFrame()— sem transições CSS para estado animado. - O cursor viaja em um caminho suave (smoothstep); a ondulação e a mudança de estado são disparadas no mesmo quadro de clique.
- Cada tela emoldurada (navegador/dispositivo) com uma URL real e tema consistente.
- Um movimento de cada vez (câmera ou cursor); zoom ≤ 2×, segure por 2–4s.
- Uma legenda de benefício por batida, no terço inferior seguro para título; as transições usam uma linguagem consistente.
Entregar e verificar (stills renderizados → MP4)
Ajudante empacotado (
scripts/): organize seus stills comscripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png, depois afirme a codificação comscripts/probe-mp4.sh out.mp4 [WxH] [fps]. Consultescripts/README.md.
Este é um nível pesado: a entrega é um MP4, não uma página ao vivo. A demonstração consome os ativos do usuário — capturas de tela/designs, logotipo opcional — então a verificação é principalmente a imagem certa realmente carregou e pousou no quadro.
Contrato de saída:
- Um projeto Remotion com a composição registrada (
<Composition>+ zodschema+defaultProps); cada valor de cursor/zoom/transição é uma função pura deuseCurrentFrame()(sem transições CSS, timers,Date.now()). - Capturas de tela carregadas via
staticFile(), protegidas comdelayRender/continueRenderpara que o PNG 2× (e qualquer fonte web) esteja presente antes que o quadro seja renderizado — caso contrário, a tela aparece em branco no meio da demonstração. - Entrega = o
out/*.mp4renderizado mais o projeto (re-renderize quando a captura de tela mudar).
Loop de verificação — renderizar stills → inspecionar → codificar. PNGs baratos primeiro, vídeo somente quando estiverem corretos.
# Stills exatos do quadro COM AS PROPS QUE VOCÊ ENTREGARÁ (caminhos de captura de tela reais), não defaultProps
npx remotion still ProductDemo out/f-settle.png --frame=15 --props=demo.json # tela emoldurada, antes do movimento
npx remotion still ProductDemo out/f-click.png --frame=90 --props=demo.json # cursor no alvo + ondulação
npx remotion still ProductDemo out/f-zoom.png --frame=150 --props=demo.json # ampliado para região focal + legenda
# quadro final = durationInFrames - 1 (npx remotion compositions lê)
Inspecione cada PNG em busca de fidelidade (captura de tela correta carregada; cursor no pixel exato; ondulação do clique e mudança de estado no mesmo quadro; texto da legenda correto) E artefatos (imagem em branco/não carregada, tela fora da tela, zoom pixelado ou ultrapassando o quadro, legenda fora do terço inferior seguro para título, proporção errada/letterboxing).
# Somente após os stills estarem limpos:
npx remotion render ProductDemo out/demo.mp4 --props=demo.json
npx remotion render ProductDemo out/demo.gif --props=demo.json --codec=gif # README primeira tela de prova
Lote (um modelo, muitos produtos): ao reestilizar a demonstração por produto, verifique as props de UM produto representativo via stills antes de renderizar em lote o catálogo — capture um bug de imagem em branco ou fora da tela uma vez, não N vezes.
Antes de finalizar:
- Os stills renderizam limpos em pousar / clique / final — sem erros, capturas de tela realmente carregadas (não em branco).
- O cursor pousa no alvo exato; ondulação + mudança de estado no mesmo quadro; texto da legenda correto e no terço inferior seguro para título.
- Todo movimento é orientado por quadro — sem transições CSS / timers /
Date.now()/Math.random(). - As props entregues (caminhos de captura de tela reais) renderizam corretamente, não apenas
defaultProps. - MP4 completo codificado e reproduzível; (opcional) GIF renderizado para o README.
Arquivos de referência
references/screenshot-demo.md— uma demonstração Remotion completa e executável: capturando capturas de tela em 2× com Playwright, os componentes de moldura de navegador/telefone, uma composição de várias etapas que percorre um cursor por várias telas com ondulações de clique e um comando de renderização.references/annotation-and-transitions.md— máscaras de holofote