GSAP com Vue, Svelte e Outros Frameworks
Quando Usar Esta Habilidade
Aplique ao escrever ou revisar código GSAP em Vue (ou Nuxt), Svelte (ou SvelteKit) ou outros frameworks de componentes que usam um ciclo de vida (montado/desmontado). Para React especificamente, use gsap-react (hook useGSAP, gsap.context()).
Habilidades relacionadas: Para tweens e timelines, use gsap-core e gsap-timeline; para animação baseada em scroll, use gsap-scrolltrigger; para React, use gsap-react.
Princípios (Todos os Frameworks)
- Crie tweens e ScrollTriggers após o DOM do componente estar disponível (ex.: onMounted, onMount).
- Mate ou reverta eles na limpeza do unmount (ou equivalente) para que nada seja executado em nós desconectados e não haja vazamentos.
- Escopo de seletores para a raiz do componente, para que
.boxe similares correspondam apenas a elementos dentro daquele componente, não ao resto da página.
Vue 3 (Composition API)
Veja examples/vue/ para um projeto Vite + Vue 3 executável demonstrando esses padrões.
Use onMounted para executar GSAP após o componente estar no DOM. Use onUnmounted para limpeza.
import { onMounted, onUnmounted, ref } from "vue";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger); // uma vez por aplicativo, ex.: em main.js
export default {
setup() {
const container = ref(null);
let ctx;
onMounted(() => {
if (!container.value) return;
ctx = gsap.context(() => {
gsap.to(".box", { x: 100, duration: 0.6 });
gsap.from(".item", { autoAlpha: 0, y: 20, stagger: 0.1 });
}, container.value);
});
onUnmounted(() => {
ctx?.revert();
});
return { container };
},
};
- ✅ gsap.context(scope) — passe a ref do container (ex.:
container.value) como segundo argumento para que seletores como.itemtenham escopo naquela raiz. Todas as animações e ScrollTriggers criados dentro do callback são rastreados e revertidos quando ctx.revert() é chamado. - ✅ onUnmounted — sempre chame ctx.revert() para que tweens e ScrollTriggers sejam mortos e os estilos inline revertidos.
Vue 3 (script setup)
Mesma ideia com <script setup> e refs:
<script setup>
import { onMounted, onUnmounted, ref } from "vue";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
const container = ref(null);
let ctx;
onMounted(() => {
if (!container.value) return;
ctx = gsap.context(() => {
gsap.to(".box", { x: 100 });
gsap.from(".item", { autoAlpha: 0, stagger: 0.1 });
}, container.value);
});
onUnmounted(() => {
ctx?.revert();
});
</script>
<template>
<div ref="container">
<div class="box">Box</div>
<div class="item">Item</div>
</div>
</template>
Nuxt 4
Veja
examples/nuxt/para um projeto Nuxt 4 executável com registro de plugins, carregamento lazy e padrões seguros para SSR.
Use um composable reutilizável para registrar plugins GSAP e também para carregar lazy plugins que não são amplamente usados na sua aplicação:
// composables/useGSAP.ts
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
const PLUGINS = [
"CSSRulePlugin",
"CustomBounce",
"CustomEase",
"CustomWiggle",
"Draggable",
"DrawSVGPlugin",
"EaselPlugin",
"EasePack",
"Flip",
"GSDevTools",
"InertiaPlugin",
"MorphSVGPlugin",
"MotionPathHelper",
"MotionPathPlugin",
"Observer",
"Physics2DPlugin",
"PhysicsPropsPlugin",
"PixiPlugin",
"ScrambleTextPlugin",
"ScrollSmoother",
"ScrollToPlugin",
"ScrollTrigger",
"SplitText",
"TextPlugin",
] as const;
type Plugins = (typeof PLUGINS)[number];
// Para carregar dinamicamente todos os plugins GSAP
const pluginMap = {
CustomEase: () => import("gsap/CustomEase"),
Draggable: () => import("gsap/Draggable"),
CSSRulePlugin: () => import("gsap/CSSRulePlugin"),
EaselPlugin: () => import("gsap/EaselPlugin"),
EasePack: () => import("gsap/EasePack"),
Flip: () => import("gsap/Flip"),
MotionPathPlugin: () => import("gsap/MotionPathPlugin"),
Observer: () => import("gsap/Observer"),
PixiPlugin: () => import("gsap/PixiPlugin"),
ScrollToPlugin: () => import("gsap/ScrollToPlugin"),
ScrollTrigger: () => import("gsap/ScrollTrigger"),
TextPlugin: () => import("gsap/TextPlugin"),
DrawSVGPlugin: () => import("gsap/DrawSVGPlugin"),
Physics2DPlugin: () => import("gsap/Physics2DPlugin"),
PhysicsPropsPlugin: () => import("gsap/PhysicsPropsPlugin"),
ScrambleTextPlugin: () => import("gsap/ScrambleTextPlugin"),
CustomBounce: () => import("gsap/CustomBounce"),
CustomWiggle: () => import("gsap/CustomWiggle"),
GSDevTools: () => import("gsap/GSDevTools"),
InertiaPlugin: () => import("gsap/InertiaPlugin"),
MorphSVGPlugin: () => import("gsap/MorphSVGPlugin"),
MotionPathHelper: () => import("gsap/MotionPathHelper"),
ScrollSmoother: () => import("gsap/ScrollSmoother"),
SplitText: () => import("gsap/SplitText"),
} as const;
type PluginMap = typeof pluginMap;
type Plugins = keyof PluginMap;
// Resolve o tipo do módulo para uma chave específica e então pega a exportação nomeada correspondente
// isso permite ter as definições de tipo para autocompletar no seu editor de código
type PluginModule<K extends Plugins> = Awaited<ReturnType<PluginMap[K]>>;
type PluginExport<K extends Plugins> = PluginModule<K>[K & keyof PluginModule<K>];
export default function () {
// Registre todos os plugins GSAP que você quiser neste ponto
gsap.registerPlugin(ScrollTrigger);
/*
Se você quiser carregar lazy alguns plugins que não são
amplamente usados no seu aplicativo (por exemplo, em apenas alguns
componentes ou uma única rota), você pode usar este método
*/
async function lazyLoadPlugin<K extends Plugins>(plugin: K): Promise<PluginExport<K>> {
const loader = pluginMap[plugin];
const m = await loader();
const p = (m as any)[plugin];
gsap.registerPlugin(p);
return p;
}
return {
gsap,
ScrollTrigger,
lazyLoadPlugin,
};
}
Acesse em componentes via useGSAP():
const { gsap, ScrollTrigger, lazyLoadPlugin } = useGSAP();
- ✅
useGSAP()fornece acesso tipado à instância do gsap e ao método de carregamento lazy. - ✅ Carregue lazy qualquer plugin (SplitText, MorphSVG, etc.) que não seja amplamente usado no seu aplicativo para reduzir o tamanho inicial do bundle.
- ✅ Use gsap.context(scope) e onUnmounted → ctx.revert() nos componentes, igual ao Vue 3.
Svelte
Use onMount para executar GSAP após o DOM estar pronto. Use a função de limpeza retornada do onMount (ou rastreie o contexto e limpe em um bloco reativo / destroy do componente) para reverter. Svelte 5 usa um ciclo de vida diferente; o mesmo princípio se aplica: crie em "mounted" e reverta em "destroyed".
<script>
import { onMount } from "svelte";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
let container;
onMount(() => {
if (!container) return;
const ctx = gsap.context(() => {
gsap.to(".box", { x: 100 });
gsap.from(".item", { autoAlpha: 0, stagger: 0.1 });
}, container);
return () => ctx.revert();
});
</script>
<div bind:this={container}>
<div class="box">Box</div>
<div class="item">Item</div>
</div>
- ✅ bind:this={container} — obtenha uma referência ao elemento raiz para passá-lo para gsap.context(scope).
- ✅ return () => ctx.revert() — o onMount do Svelte pode retornar uma função de limpeza; chame ctx.revert() nela para que a limpeza seja executada quando o componente for destruído.
Escopo de Seletores
Não use seletores globais que possam corresponder a elementos fora do componente atual. Sempre passe o scope (elemento container ou ref) como segundo argumento para gsap.context(callback, scope) para que qualquer seletor executado dentro do callback seja limitado àquela subárvore.
- ✅ gsap.context(() => { gsap.to(".box", ...) }, containerRef) —
.boxé pesquisado apenas dentro decontainerRef. - ❌ Executar gsap.to(".box", ...) sem um escopo de contexto em um componente pode afetar outras instâncias ou o resto da página.
Limpeza de ScrollTrigger
Instâncias de ScrollTrigger são criadas quando você usa a configuração scrollTrigger em um tween/timeline ou ScrollTrigger.create(). Elas são incluídas em gsap.context() e revertidas quando você chama ctx.revert(). Portanto:
- Crie ScrollTriggers dentro do mesmo callback gsap.context() que você usa para tweens.
- Chame ScrollTrigger.refresh() após mudanças de layout (ex.: após carregamento de dados) que afetem as posições dos triggers; em Vue/Svelte isso geralmente significa após o DOM ser atualizado (ex.: nextTick no Vue, tick no Svelte, ou após carregamento de conteúdo assíncrono).
Quando Criar vs Matar
| Ciclo de Vida | Ação |
|---|---|
| Montado | Crie tweens e ScrollTriggers dentro de gsap.context(scope). |
| Desmontar / Destruir | Chame ctx.revert() para que todas as animações e ScrollTriggers naquele contexto sejam mortos e os estilos inline revertidos. |
Não crie animações GSAP no setup do componente ou em um script síncrono de nível superior que execute antes do elemento raiz existir. Aguarde onMounted / onMount (ou equivalente) para que a ref do container esteja no DOM.
Não Faça
- ❌ Crie tweens ou ScrollTriggers antes do componente ser montado (ex.: no setup sem onMounted); os nós do DOM podem não existir ainda.
- ❌ Use strings de seletor sem um scope (passe o container para gsap.context() como segundo argumento) para que seletores não correspondam a elementos fora do componente.
- ❌ Pule a limpeza; sempre chame ctx.revert() no onUnmounted / no retorno de onMount para que animações e ScrollTriggers sejam mortos quando o componente for destruído.
- ❌ Registre plugins dentro do corpo de um componente que executa a cada renderização (não causa dano, é apenas desperdício); registre uma vez no nível do aplicativo.
Saiba Mais
- Habilidade gsap-react para padrões específicos do React (useGSAP, contextSafe).