graphic-gif
Gera um GIF animado em loop a partir de animações CSS ou de uma API de IA de imagem para vídeo. Saída: animation.gif.
Ao contrário de todas as outras skills graphic- que geram PNG ou PDF estáticos, esta skill gera um .gif animado. Usa animações CSS @keyframes capturadas quadro a quadro via Playwright e a Web Animations API (Opção A, padrão), ou uma pipeline de IA de imagem para vídeo via Kling (Opção B).
Regras Críticas (leia antes de cada geração)
- O padrão é css-animated. Nunca use
ai-generateda menos que seja explicitamente solicitado. - A tela é 800×800px quadrada. Todos os valores
clamp()calculados em 800px (1vw = 8px). - HTML autossuficiente. Todo CSS inline em
<style>. Apenas CDN de fonte<link>como dependência externa. - Nunca despeje HTML no chat. Salve em arquivo, mostre apenas o resumo.
- A captura de quadros usa a Web Animations API. NÃO loops
setTimeout, NÃO truques comanimation-delay. - Contagem exata de quadros:
Math.floor(duracao_segundos * fps)quadros. O quadro emt=duracao_msNÃO DEVE ser capturado — ele duplicat=0e causa uma trepidação visível no ponto de loop. - Sem caixas placeholder. Apenas visuais gerados por CSS. Sem elementos "a imagem vai aqui".
- Paletas mais simples = arquivos menores. Use:
clean-slate,terminal,electric-burst,brutalist. - Sem
animation-delaypara escalonamento. Incorpore o escalonamento nas porcentagens dos@keyframes— a busca de quadros lida com o tempo. - Comprometa-se com a direção de design antes de escrever CSS. Tom, elemento de assinatura, estilo de movimento, detalhe inesquecível — tudo decidido antes da primeira linha de código.
Passo 1: Entrada
Obrigatório: prompt (descrição do conteúdo E instrução de movimento)
Opcionais com padrões:
| Parâmetro | Padrão | Opções |
|---|---|---|
| animation_type | css-animated | css-animated / ai-generated |
| duration | 3.0 | segundos |
| fps | 12 | quadros por segundo |
| loop | true | true / false |
| style | clean-slate | clean-slate / terminal / electric-burst / brutalist |
| dimensions | 800x800 | LxA em pixels |
| optimization | balanced | quality / balanced / filesize |
Se o prompt estiver ausente ou faltar descrição de movimento, pergunte exatamente:
"O que o GIF deve mostrar? Descreva o conteúdo E o movimento (ex.: 'Contagem de estatísticas: 73% dos compradores leem 3+ conteúdos antes da compra. Efeito de máquina de escrever, um caractere por vez. Estilo: terminal. 3 segundos, 12fps.')
Configurações principais (todas opcionais, padrões mostrados):
- animation_type: css-animated (padrão) ou ai-generated
- duration: 3.0 segundos
- fps: 12
- loop: true
- style: clean-slate (opções: clean-slate / terminal / electric-burst / brutalist)
- dimensions: 800x800
- optimization: balanced (opções: quality / balanced / filesize)"
Se todas as informações necessárias estiverem presentes → pule diretamente para o Passo 2.
Passo 2: Arquitetura Interna (nunca mostrada ao usuário)
Para css-animated:
- Escolha o tipo de animação entre:
fade-in,slide-in,typewriter,counter,pulse,loop-scroll - Leia
references/animation-library.md— encontre a especificação HTML/CSS completa do tipo escolhido - Leia
references/style-presets.md— carregue o bloco de tokens CSS do estilo escolhido - Calcule a contagem de quadros:
Math.floor(duracao_segundos * fps)— anote este número - Comprometa-se com a direção de design:
| Decisão | Derivar de |
|---|---|
| Tom | Registro emocional para o público (mecânico / acolhedor / elétrico / profissional) |
| Elemento de assinatura | UM dispositivo visual usado consistentemente (piscar de cursor, número fantasma, sobreposição de linha de varredura, borda de destaque) |
| Estilo de movimento | Filosofia de curva de facilitação para este tipo (spring / linear / step / ease-in-out) |
| Detalhe inesquecível | A ÚNICA coisa que um espectador lembrará sobre este GIF |
Para ai-generated:
- Gere o HTML do quadro estático base (layout estilo pôster para a tela)
- Exporte como PNG usando screenshot
- Chame a API Kling:
POST https://api.klingai.com/v1/videos/image2videocomimage_urlepromptdescrevendo o movimento - Faça polling até a conclusão do trabalho
- Baixe o vídeo → converta para GIF com ffmpeg:
# Paleta de duas passagens para melhor qualidade de cor ffmpeg -i input.mp4 -vf "fps=12,scale=800:800:flags=lanczos,palettegen=stats_mode=diff" palette.png ffmpeg -i input.mp4 -i palette.png -vf "fps=12,scale=800:800:flags=lanczos,paletteuse=dither=bayer:bayer_scale=5" output.gif
Passo 3: Geração de HTML (caminho css-animated)
Leia references/animation-library.md e references/style-presets.md antes de gerar.
Base da tela — obrigatório em todo GIF:
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body {
width: 800px;
height: 800px;
overflow: hidden;
background: var(--bg);
font-family: var(--font-body);
}
.canvas {
width: 800px;
height: 800px;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
Regras de animação:
animation-fill-mode: forwards(ouboth) em TODOS os elementos animados- Funções de temporização por tipo:
typewriter→steps(N, end)onde N = contagem exata de caracterescounter→linearfade-in→cubic-bezier(0.22, 1, 0.36, 1)(ease-out)slide-in→cubic-bezier(0.34, 1.56, 0.64, 1)(spring overshoot)pulse→ease-in-outcomanimation-iteration-count: infiniteloop-scroll→linearcomanimation-iteration-count: infinite
- Para animações de disparo único (fade-in, slide-in, typewriter, counter):
animation-iteration-count: 1— o looping acontece no nível do GIF - Sem
animation-delay— o escalonamento é incorporado nas porcentagens dos@keyframes
Cálculo de N para typewriter: Conte cada caractere, incluindo espaços, pontuação, números:
- "73% dos compradores" = 18 caracteres →
steps(18, end) - "Olá, Mundo!" = 11 caracteres →
steps(11, end)
@property CSS para contador (obrigatório para o tipo counter):
@property --num {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
.counter {
animation: countUp var(--duration) linear forwards;
counter-reset: num var(--num);
}
.counter::after { content: counter(num); }
@keyframes countUp {
from { --num: 0; }
to { --num: var(--target); }
}
Loop-scroll: o conteúdo DEVE ser duplicado:
<!-- 4 itens originais + 4 itens duplicados -->
<div class="ticker-track" style="--item-count: 4;">
[item1][item2][item3][item4][item1][item2][item3][item4]
</div>
translateX(0 → -50%) com linear infinite.
Regras de qualidade de design (do compromisso no Passo 2):
- O elemento de assinatura nomeado DEVE estar presente no CSS/HTML (não apenas descrito)
- Tipografia: contraste de peso mínimo 2:1 (ex.: 700 vs 400) entre texto de exibição e texto de suporte
- Fundo: sem branco puro
#fffpara estilos escuros — use o valor exato--bgda predefinição - Para estilo terminal: adicione sobreposição de linha de varredura
::aftercomrepeating-linear-gradientemopacity: 0.03 - Para brutalist: borda grossa
4px solid #000ou4px solid var(--accent)no elemento-chave - Detalhe inesquecível: se exigir um elemento extra — adicione-o agora
Passo 4: Auto-QA (corrija todas as falhas antes do Passo 5)
Tela:
-
bodye.canvasexatamente 800×800px (ou dimensões especificadas) -
overflow: hiddenem ambosbodye.canvas - Nenhum elemento ultrapassando o limite da tela
Animações:
- NENHUM
animation-delayem lugar algum — o escalonamento está nas porcentagens dos@keyframes - Todas as animações começam em
t=0(a Web Animations API buscará a partir daí) -
animation-fill-mode: forwardsoubothem todos os elementos animados - Animações de disparo único:
animation-iteration-count: 1 - Animações infinitas (pulse, loop-scroll):
animation-iteration-count: infinite
Verificações específicas de tipo:
- Typewriter: N em
steps(N, end)= contagem exata de caracteres da string de texto - Counter:
@property --numdeclarado comsyntax: '<integer>'einitial-value: 0 - Counter:
counter-reset: num var(--num)e::after { content: counter(num) } - Loop-scroll: conteúdo duplicado exatamente uma vez no HTML
Design:
- Sem caixas placeholder
- Tokens de predefinição de estilo aplicados de
references/style-presets.md— sem cores hexadecimais soltas - Elemento de assinatura nomeado no Passo 2 está realmente presente no HTML/CSS
- Detalhe inesquecível do Passo 2 está realmente implementado
- CDN de fonte
<link>presente para a fonte do estilo escolhido
Passo 5: Exportação
Determine o slug a partir do prompt (kebab-case, ≤30 caracteres). Crie o diretório de saída:
mkdir -p [slug]
Salve o HTML:
[slug]/animation.html
Abra no navegador para uma verificação visual rápida:
open [slug]/animation.html
Execute o script de exportação (substitua [skill-root] pelo caminho real para esta skill):
bash [skill-root]/scripts/export-gif.sh \
[slug]/animation.html \
[slug]/animation.gif \
--duration [duration] \
--fps [fps] \
[--no-loop se loop=false] \
--optimization [optimization] \
--width [W] \
--height [H]
O script:
- Instala
gifenc,sharp(oujimp), eplaywrightem um diretório temporário - Baixa o Chromium se não estiver em cache
- Executa
capture-and-encode.mjs— pausa animações, busca cada quadro, tira screenshot, monta o GIF - Executa a passagem de otimização
gifsiclese disponível - Informa o tamanho do arquivo e abre o resultado
Se o script de exportação não for encontrado em [skill-root]/scripts/export-gif.sh, verifique se a skill foi instalada com a pasta scripts/ intacta.
Passo 6: Resumo da Saída
Mostre após exportação bem-sucedida:
## GIF: [descrição em 1 linha]
Data: [AAAA-MM-DD] | Estilo: [style] | Animação: [type] | [duration]s @ [fps]fps
Dimensões: [LxA] | Quadros: [N] | Loop: [true/false]
Arquivos
Fonte: [slug]/animation.html
Saída: [slug]/animation.gif
Tamanho: [X] KB
Lista de verificação
- [ ] Preview faz loop limpo no ponto inicial/final (sem trepidação)
- [ ] Texto legível no tamanho de exibição pretendido
- [ ] Tamanho de arquivo apropriado: e-mail <500KB / redes sociais <3MB
Caminho de IA (Opção B)
Use apenas quando animation_type: ai-generated for explicitamente especificado.
Requisitos:
- Chave de API Kling no ambiente:
KLING_API_KEY(66 créditos gratuitos/dia, sem cartão de crédito para o nível gratuito) ffmpeginstalado localmente para conversão de vídeo para GIF
Fluxo de trabalho:
- Gere um HTML de quadro estático base correspondente ao prompt (estilo pôster/gráfico nas dimensões especificadas)
- Exporte o quadro estático como PNG:
# Screenshot rápido via Playwright node -e " const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: W, height: H } }); await page.goto('file://[slug]/animation.html'); await page.screenshot({ path: '[slug]/base-frame.png' }); await browser.close(); })(); " - Envie para o endpoint de imagem para vídeo da Kling
- Converta o resultado para GIF com paleta de duas passagens do ffmpeg
- Aplique otimização gifsicle
Quando a Kling estiver indisponível: Reverta para css-animated com uma nota ao usuário: "A geração por IA requer uma chave de API Kling (KLING_API_KEY). Revertendo para css-animated. Defina a chave para habilitar a geração por IA."
Dicas de Prompt (mostre quando o usuário pedir orientação)
"Descreva o movimento, não apenas o conteúdo. 'Contagem de estatísticas uma por uma' é melhor que 'mostrar estatísticas'."
"Mantenha simples para o tamanho do arquivo. 1–3 elementos animados e um fundo sólido."
"Pense em loops. A animação deve fluir invisivelmente do final de volta ao início."
"Especifique o tipo de animação explicitamente.
typewriterecountersão os mais eficazes para redes sociais."✅ Bom: "Criar um GIF animado, css-animated, efeito typewriter. Texto: '73% dos compradores B2B leem 3+ conteúdos antes de contatar vendas.' Cada caractere é digitado um por vez. Estilo: terminal. 3 segundos, 12fps, loop=true."
❌ Ruim: "fazer um gif animado de dicas de marketing"