Extração de Movimento de Vídeo e Protótipo (video-to-gsap)
1. Propósito e Gatilhos
Microinterações fluidas e movimento cinematográfico diferenciam experiências de comércio de alto nível. No entanto, animações web mal implementadas causam bloqueio de thread, quadros perdidos (jank), vazamentos de memória e má Interaction to Next Paint (INP). Esta habilidade governa a extração e implementação de animações suaves como manteiga a 60/120 FPS.
Gatilhos:
- Quando o usuário fornecer um clipe de vídeo, GIF ou protótipo de movimento Figma.
- Ao animar sobreposições de carrinho, transições de página, expansões de acordeão, hovers em cartões de produto ou celebrações de conclusão de checkout.
- Ao criar animações acionadas por scroll com ScrollTrigger.
2. Protocolo de Desconstrução de Movimento
Ao analisar um clipe de movimento, identifique sistematicamente:
- A Sequência de Coreografia:
- Cena / Gatilho: Qual ação inicia o movimento (hover, scroll, mudança de rota, clique)?
- Antecipação: Há um contra-movimento sutil antes do movimento principal?
- Ação e Deslocamento: Quais elementos se movem, em quais direções, a que distância (px, rem ou %)?
- Estabilização e Seguimento: Ele ultrapassa (spring/elastic) ou desacelera suavemente (
power3.out,expo.out)?
- Detalhamento de Temporização:
- Duração de cada elemento (ex: entrada do drawer:
450ms, fade do fundo:300ms). - Offset de escalonamento entre filhos escalonados (ex: cartões de grade de produto:
0.06sde escalonamento).
- Duração de cada elemento (ex: entrada do drawer:
- Assinatura de Easing:
- Linear: estritamente para loops contínuos (ex: marquee).
- Ease-out (
power2.out,expo.out): para entradas de UI (entrada rápida, estabilização suave). - Ease-in (
power2.in): para saídas de UI (acelera para fora da tela). - Ease-in-out (
power3.inOut): para transformações de estado que permanecem na vista.
3. Padrões de Desempenho e Aceleração de Hardware
Regra 1: Anime Apenas Propriedades de Composição
- Permitido:
transform(x,y,xPercent,yPercent,scale,rotation,skew) eopacity(ouautoAlphado GSAP). - Estritamente Proibido: Animar propriedades de layout ou pintura (
width,height,top,left,right,bottom,margin,padding,border-width). Estas forçam a CPU do navegador a recalcular a geometria do layout e repintar pixels a cada quadro, causando degradação do INP e perda de quadros.
Regra 2: Zero Vazamentos de Memória com useGSAP com Escopo
- Sempre use o pacote oficial
@gsap/reacte o hookuseGSAP. - Forneça um
scopecontainer ref. Cada seletor dentro será automaticamente escopado, e todas as timelines, tweens e instâncias ScrollTrigger serão limpas e mortas quando o componente desmontar. - Isso previne listeners de eventos zumbis e retenção de memória em Single Page Apps e ciclos de fast-refresh do React 19.
Regra 3: Elimine FOUC (Flash de Conteúdo Não Estilizado)
- Use
autoAlphaem vez deopacity.autoAlphadefinevisibility: hiddenquando a opacidade chega a 0, prevenindo cliques fantasmas e flashes de renderização antes da hidratação. - Alternativamente, declare estados ocultos padrão no CSS e revele-os com
gsap.fromTo.
4. Blueprint de Implementação em Produção
"use client";
import { useRef } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";
import { ScrollTrigger } from "gsap/ScrollTrigger";
// Garanta que os plugins sejam registrados com segurança
if (typeof window !== "undefined") {
gsap.registerPlugin(useGSAP, ScrollTrigger);
}
interface ProductShowcaseProps {
readonly items: Array<{ id: string; title: string; price: string }>;
}
export function ProductShowcase({ items }: ProductShowcaseProps) {
const containerRef = useRef<HTMLDivElement | null>(null);
useGSAP(
() => {
// Timeline de entrada acelerada por hardware
const tl = gsap.timeline({
defaults: {
ease: "expo.out",
duration: 0.8,
},
});
tl.from(".showcase-header", {
autoAlpha: 0,
y: 30,
clearProps: "all", // Limpa estilos inline pós-animação
}).from(
".product-card",
{
autoAlpha: 0,
y: 40,
scale: 0.96,
stagger: 0.08,
duration: 0.7,
clearProps: "transform,opacity",
},
"-=0.4" // Sobreposição perfeita
);
},
{ scope: containerRef } // Limite de limpeza com escopo
);
return (
<section ref={containerRef} className="w-full py-16 px-6">
<div className="showcase-header max-w-4xl mx-auto text-center mb-12">
<h2 className="text-h1 font-bold text-content-primary">Coleção em Destaque</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-7xl mx-auto">
{items.map((item) => (
<article
key={item.id}
className="product-card will-change-transform bg-surface-elevated rounded-lg p-6 border border-border-subtle"
>
<h3 className="text-h3 font-semibold text-content-primary">{item.title}</h3>
<p className="text-content-secondary mt-2">{item.price}</p>
</article>
))}
</div>
</section>
);
}
5. Conformidade com Movimento Reduzido
Sempre respeite os usuários que preferem movimento reduzido:
const { contextSafe } = useGSAP({ scope: containerRef });
// Verifique a preferência de acessibilidade
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (prefersReducedMotion) {
// Renderiza instantaneamente no estado final de descanso sem animação
gsap.set([".showcase-header", ".product-card"], { autoAlpha: 1, y: 0, scale: 1 });
return;
}