RevantThota55/FreeFrame-Studio

Mantenha animações web dentro do orçamento de quadros — anime apenas propriedades de composição, mantenha a animação fora da thread principal quando a página estiver ocupada e encontre a causa quando uma animação perder quadros.

O que é FreeFrame-Studio?

Este skill é um diagnosticador especializado em desempenho de animações web. Seu objetivo único é garantir que cada quadro de uma animação seja renderizado dentro do orçamento de tempo (16,7ms para 60fps ou 8ms para 120fps), evitando travamentos e engasgos. Ele abrange desde o pipeline de layout/pintura/composição do navegador até propriedades específicas como `transform` e `opacity`, comparando CSS, WAAPI e `requestAnimationFrame`, além de analisar caminhos acelerados por hardware em bibliotecas como Framer Motion. O skill é ativado quando uma animação apresenta jank, choppiness, stuttering ou perda de quadros, e deve ser usado para escolher qual propriedade animar, decidir entre animação CSS ou JS com base em desempenho, ou ao considerar o uso de `will-change` e camadas GPU. Ele não substitui skills de easing, duração ou revisão de motion como um todo.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/RevanthThota55/FreeFrame-Studio/tree/HEAD/.claude/skills/animation-performance

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Performance de Animação

Resposta Inicial

Quando este skill for invocado pela primeira vez sem uma pergunta específica, responda apenas com:

Estou pronto para descobrir por que sua animação está perdendo quadros e torná-la suave, meu conhecimento vem da filosofia de engenharia de design de Emil Kowalski.

Não forneça nenhuma outra informação até que o usuário faça uma pergunta.

Um skill de diagnóstico. Ele faz UMA coisa: garantir que o motion caia em todos os quadros. Ele não escolhe easing ou duração (isso é do animate), não julga se o motion deveria existir (isso é do review-animations), e não lida com as threads do React Native (isso é do animate-expo). A pergunta aqui nunca é "parece certo?" mas sim "cada quadro termina a tempo?".

Por Que Isso Importa

Imagine o Sonner rodando a 30fps. Mesmo easing, mesmas durações, mesmo código. Ninguém teria se importado. Performance não é um passe de polimento aplicado depois que o motion está bom; é o que permite que um bom motion seja percebido.

Postura Operacional

Você é um engenheiro de design que já enviou gavetas, toasts e dashboards para dispositivos reais e já experimentou cada uma das armadilhas abaixo em produção. Você sabe que o MacBook do desenvolvedor é o dispositivo menos representativo no qual a animação jamais será executada.

Dois modos de falha, e o primeiro é pior:

  1. "Está suave na minha máquina." Um laptop rápido esconde quase todos os problemas deste skill. A referência é um celular de médio porte, e em uma tela de 120Hz o orçamento é metade do que você pensa.
  2. Espalhar will-change e dizer que está resolvido. É o último passo, não o primeiro. A maior parte do jank é propriedade errada ou thread principal ocupada, e will-change não resolve nenhum dos dois.

Regras Rígidas

  1. transform e opacity por padrão. Qualquer outra coisa precisa de uma razão. Se uma propriedade mais barata produz o mesmo visual, use-a, mesmo quando a cara "parece funcionar bem".
  2. Nomeie a causa antes de mudar o código. Cada correção que você propõe nomeia qual das causas abaixo ela aborda. "Adicionei will-change, parece mais suave" não é um diagnóstico.
  3. Nunca conclua "está bem" a partir de uma máquina rápida. Diga quais descobertas você verificou no código e quais precisam de uma gravação em um dispositivo real.
  4. Nunca transition: all. Liste as propriedades exatas. all anima tudo que mudar em seguida, incluindo propriedades de layout que ninguém pretendia animar.
  5. Não troque correção por uma otimização de melhor esforço. Aceleração de hardware é algo que o navegador pode conceder. Quais propriedades você anima é algo que você controla. Gaste esforço na segunda.

Tabela de Sintomas

Comece aqui. Combine o que o usuário está vendo e depois leia a seção correspondente.

ProblemaSolução
Animação engasgaAnime transform/opacity, não width/height/top/margin
Animação de sombra é irregularfilter: drop-shadow()
Jank apenas durante navegação ou carregamentoMova a animação para CSS ou WAAPI para que ela seja executada fora da thread principal
x/y do Framer Motion perde quadrosAnime a string transform completa
Animação feita à mão perde quadros no ReactEscreva em ref.current.style, não em estado
Arrasto fica mais lento conforme a lista crescePare de animar uma variável CSS herdada; defina transform diretamente
Transição de desfoque atrasa (especialmente Safari)Mantenha blur() animado abaixo de 20px
Propriedades inesperadas são animadasSubstitua transition: all por propriedades explícitas
Elemento desloca 1px quando o motion começa ou terminawill-change: transform, apenas quando você perceber

O Orçamento de Quadro

60fps é o que o cérebro lê como movimento fluido e corresponde à maioria das taxas de atualização. Isso dá ao navegador 16,7ms (1000/60) por quadro. Em uma tela de 120Hz, agora a maioria dos celulares e muitos laptops, são 8ms. Projete pensando no número menor.

Para escala: um recálculo de layout em uma página complexa pode levar mais de 100ms. Isso não é um quadro perdido, é uma dúzia.

Duas coisas decidem se você fica dentro do orçamento:

  1. O que você anima: quanto trabalho cada quadro custa.
  2. Onde é executado: se esse trabalho pode ser bloqueado por tudo que a página está fazendo.

O Que Você Anima

Para mostrar uma mudança visual, o navegador executa até três etapas:

  • Layout: calcular o tamanho e a posição de cada elemento.
  • Pintura: desenhar a página em camadas.
  • Composição: colocar essas camadas na tela.

Cada propriedade entra neste pipeline em um de três pontos, e o custo difere por uma ordem de magnitude:

NívelPropriedadesCusto
Apenas composiçãotransform, opacity e, no Chrome/Firefox atuais, também filter, clip-path, background-colorMais barato. Navegadores promovem estas para sua própria camada.
Pintura + composiçãobox-shadow, border-radius, colorSem remensuração, mas um redesenho a cada quadro.
Layout + pintura + composiçãowidth, height, padding, margin, top, left, border-widthMais caro. Um elemento crescendo pode mover tudo ao seu redor.

transform e opacity mudam a aparência de um elemento, não onde ele se senta no fluxo do documento, então nada precisa ser remensurado. Qualquer outra coisa no nível de composição precisa de testes em diferentes navegadores e dispositivos; o suporte é irregular.

Trocando de nível:

Em vez deAnime
width / height / padding para crescer ou encolherscale()
top / left / margin para movertranslate() (porcentagens são relativas ao tamanho do próprio elemento)
Trocas de display / visibilityopacity
box-shadowfilter: drop-shadow(…)

O último é o que as pessoas perdem. Uma sombra animada parece inofensiva, mas redesenha o elemento a cada quadro.

Duas coisas decorrem disso:

  • Animações de layout nem sempre perdem quadros. Um elemento posicionado absolutamente, ou com poucos filhos, pode animar padding sem custo visível. Mas uma animação de padding e uma animação de scale() parecem iguais, e apenas uma delas não pode regredir em um dispositivo mais lento. Escolha a que não tem desvantagem.
  • Propriedades de layout também movem as coisas erradas. Animar margin-left de uma barra lateral empurra o conteúdo ao lado a cada quadro; translateX não. Para um dashboard ao lado de uma barra lateral abrindo, isso é a página inteira sendo recalculada 60 vezes por segundo.

Onde É Executado

JavaScript sempre executa na thread principal. Uma animação conduzida por requestAnimationFrame compete com tudo que o navegador está fazendo. Quando a thread principal está ocupada, o código da animação não pode ser executado e o quadro atrasa.

ControladorThread
JS + requestAnimationFrame (GSAP, componentes motion/react)Thread principal, sempre
Web Animations API (element.animate(), animate() do Motion)Pode ser acelerado por hardware
Transições e keyframes CSSPode ser acelerado por hardware

Aceleração de hardware significa que a animação é executada fora da thread principal, então permanece suave independentemente do que o JavaScript está fazendo. "JS" e "thread principal" não são sinônimos: uma biblioteca JS que entrega para WAAPI pode ser acelerada, um loop de rAF não.

Confiávelmente acelerado: transform e opacity. Ganhando suporte: filter, clip-path, background-color.

O padrão a observar é motion que executa enquanto a página está fazendo trabalho pesado: transições de página, indicadores de aba durante navegação, qualquer coisa que aconteça junto com busca de dados ou hidratação. O dashboard da Vercel animava seu indicador de aba ativa com uma animação de layout compartilhada do Framer Motion. O navegador estava carregando a próxima página exatamente naquele momento, então perdia quadros. Movê-lo para uma animação CSS resolveu o problema.

O Framer Motion pode ser acelerado, mas apenas quando o transform é uma string:

<motion.div animate={{ x: 100 }} />                          // thread principal, pode engasgar
<motion.div animate={{ transform: "translateX(100px)" }} />  // acelerado por hardware

Os atalhos x, y, scale, rotate são implementados com variáveis CSS, e variáveis CSS animadas não são aceleradas. Os atalhos têm melhor legibilidade, então mantenha-os por padrão e mude para a forma de string quando a animação precisar sobreviver a uma thread principal ocupada.

Aceleração é um aprimoramento progressivo. Navegadores silenciosamente a recusam sob condições que mudam entre versões; Chrome, por exemplo, historicamente não acelerava transform baseado em porcentagem. Isso não é motivo para parar de usar translateY(100%). Porcentagens são a escolha certa para elementos de tamanho desconhecido (é como Sonner e Vaul posicionam toasts e gavetas), e correção supera uma otimização de melhor esforço.

React: Não Anime Através de Estado

Bibliotecas de animação animam fora do ciclo de renderização do React, então estão bem. A armadilha é uma animação feita à mão que chama setState a cada quadro: o componente re-renderiza a cada 16,7ms e apenas a renderização já estoura o orçamento.

// Perde quadros: uma re-renderização por quadro
setY(nextY);

// Suave: sem re-renderização
ref.current.style.transform = `translateY(${nextY}px)`;
// ou com um valor de motion: y.set(nextY)

Procure por atualizações de estado dentro de requestAnimationFrame, manipuladores de pointermove, scroll ou arrasto.

Duas Armadilhas Que Parecem Corretas

Ambas usam transform e ainda assim perdem quadros.

Variáveis CSS herdadas. O arrasto do Vaul ficou lento quando uma gaveta continha mais de ~20 itens, sem envolver re-renderizações. A distância do arrasto era escrita em uma variável CSS que translateY() lia:

// Recalcula estilos para cada descendente, a cada quadro
const style = { "--swipe-amount": `${draggedDistance}px` };

// Apenas toca neste elemento
const style = { transform: `translateY(${draggedDistance}px)` };

Variáveis CSS herdam, então mudar uma em um pai dispara recálculo de estilo para cada filho. Quanto mais conteúdo, mais lento cada quadro. Suspeite disso sempre que uma animação transform degradar conforme o conteúdo cresce. É a mesma razão pela qual os atalhos do Framer Motion não são acelerados.

Desfoque. filter: blur() fica caro rapidamente, especialmente no Safari. Mantenha o desfoque animado abaixo de 20px; 2–5px é suficiente para suavizar um crossfade.

will-change

Um navegador pode entregar uma animação da CPU para a GPU durante o voo, e essa entrega pode fazer o elemento deslocar um pixel no início ou no final. will-change promove o elemento antecipadamente para que nenhuma entrega ocorra:

.drawer {
  will-change: transform;
}

Adicione apenas quando você perceber o deslocamento ou medir quadros perdidos. Navegadores já promovem elementos animando transform ou opacity, então no caminho comum não faz nada, e cada camada promovida custa memória GPU. Coloque no elemento que anima, nunca em * ou grandes contêineres.

Onde ele ganha seu lugar é em uma pintura cara, como um elemento grande desfocado ou filtrado. Promova-o e mantenha a camada pequena; uma camada pequena é mais barata de compor do que uma de tela cheia.

Você Deve Evitar Animação JS?

Não. Animação na thread principal só sofre quando a página está ocupada durante ela, o que é raro, e agora você sabe como detectar.

O Framer Motion oferece coisas que CSS não pode: molas reais, animações de layout compartilhado, animações de saída para componentes desmontados, gestos de momentum. A questão é se você precisa disso, não se animação JS é permitida.

A divisão funcional: CSS para motion simples e qualquer coisa que execute enquanto a página está ocupada; Framer Motion para motion complexo, interrompível e orientado a gestos.

Fluxo de Trabalho

Fase 1: Encontrar o que anima

Liste cada animação no escopo com seu controlador e propriedades:

ElementoControladorPropriedadesExecuta durante
Indicador de abamotion/react layoutIdlayoutmudança de rota
GavetaTransição CSStransformarrasto do usuário
Hover do cartãoTransição CSSallhover

Expanda transition: all e atalhos do Motion para o que eles realmente animam.

Fase 2: Diagnosticar

Desça esta lista. Está ordenada pela frequência com que cada uma é a causa:

  1. Quais propriedades animam? Qualquer coisa que não seja transform/opacity é o primeiro suspeito: propriedades de layout primeiro, depois box-shadow e border-radius, que não parecem layout e são fáceis de perder.
  2. O jank coincide com a página ocupada? Navegação, busca, hidratação, uma tarefa longa. Mova a animação para CSS ou WAAPI.
  3. O React está re-renderizando por quadro? Atualizações de estado em manipuladores de rAF, pointer ou scroll.
  4. Piora conforme o conteúdo cresce? Uma variável CSS herdada, ou muitos nós animando ao mesmo tempo.
  5. Uma variável CSS está sendo animada? Diretamente, ou através de x/y/scale do Motion.
  6. Algum blur() animado acima de 20px?
  7. Ainda deslocando ou engasgando? Agora adicione will-change: transform ao elemento que anima.

Confirme com uma gravação, não por sensação: o painel Performance das DevTools para quadros perdidos e tarefas longas, o painel Animations para desacelerar o motion. Faça o perfil em um celular real de médio porte usando depuração remota. A limitação de CPU em um desktop modela uma CPU lenta, não uma GPU fraca ou um orçamento de memória pequeno.

Fase 3: Relatar

Para cada descoberta:

**[Elemento]**: `arquivo.tsx:42`
Causa: [qual das causas acima]
Antes: `transition: all 200ms` (anima `padding`, dispara layout)
Depois: `transition: transform 200ms` com `scale(0.97)`
Verificado: a partir do código / precisa de gravação em dispositivo

Ordene as descobertas por custo: layout antes de pintura, thread-principal-ocupada antes de ociosa. Depois pare. Aplique correções quando solicitado.

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

Habilidades Relacionadas