GSAP 与 Vue、Svelte 及其他框架
何时使用该技能
在编写或审查 Vue (或 Nuxt)、Svelte (或 SvelteKit) 或其他使用组件生命周期(挂载/卸载)的框架中的 GSAP 代码时应用。对于 React,请使用 gsap-react (useGSAP 钩子, gsap.context())。
相关技能: 对于补间动画和时间线,使用 gsap-core 和 gsap-timeline;对于基于滚动的动画,使用 gsap-scrolltrigger;对于 React,使用 gsap-react。
原则(所有框架)
- 创建 补间动画和 ScrollTrigger 在 组件的 DOM 可用之后(例如 onMounted、onMount)。
- 销毁或还原 它们于 卸载(或等效)清理中,以确保不会有动画在已分离的节点上运行,也不会造成内存泄漏。
- 限定选择器作用域 到组件根元素,使得
.box等选择器仅匹配该组件内部的元素,而非页面其余部分。
Vue 3 (Composition API)
查看 examples/vue/ 目录下的可运行 Vite + Vue 3 项目,以了解这些模式的实践。
使用 onMounted 在组件挂载到 DOM 后运行 GSAP。使用 onUnmounted 进行清理。
import { onMounted, onUnmounted, ref } from "vue";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger); // 每个应用只需一次,例如在 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) —— 将容器 ref(例如
container.value)作为第二个参数传入,以将.item等选择器的作用域限定在该根元素内。在回调中创建的所有动画和 ScrollTrigger 都会被跟踪,并在调用 ctx.revert() 时被还原。 - ✅ onUnmounted —— 始终调用 ctx.revert(),以便销毁补间动画和 ScrollTrigger,并还原内联样式。
Vue 3 (script setup)
与 <script setup> 和 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
查看
examples/nuxt/目录下的可运行 Nuxt 4 项目,其中包含插件注册、懒加载和 SSR 安全模式。
使用一个 可复用的 composable 来注册 GSAP 插件,同时懒加载那些在应用中不常使用的插件:
// 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];
// 用于动态加载所有 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;
// 为给定键解析模块类型,然后选取与键匹配的命名导出
// 这使得你可以在代码编辑器中获得类型定义以支持自动补全
type PluginModule<K extends Plugins> = Awaited<ReturnType<PluginMap[K]>>;
type PluginExport<K extends Plugins> = PluginModule<K>[K & keyof PluginModule<K>];
export default function () {
// 在此处注册所有你需要的 GSAP 插件
gsap.registerPlugin(ScrollTrigger);
/*
如果你想懒加载一些在应用中不广泛使用的插件
(例如只在少数组件或单个路由中使用),可以使用此方法
*/
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,
};
}
通过 useGSAP() 在组件中访问:
const { gsap, ScrollTrigger, lazyLoadPlugin } = useGSAP();
- ✅
useGSAP()提供了类型安全的 gsap 实例和懒加载方法。 - ✅ 懒加载任何插件(SplitText、MorphSVG 等) 如果它们在你的应用中不常使用,以减少初始包体积。
- ✅ 在组件中使用 gsap.context(scope) 和 onUnmounted → ctx.revert(),与 Vue 3 相同。
Svelte
使用 onMount 在 DOM 就绪后运行 GSAP。使用 onMount 返回的清理函数(或跟踪 context 并在响应式块/组件销毁时清理)来还原。Svelte 5 使用不同的生命周期;但相同原则适用:在“挂载”时创建,在“销毁”时还原。
<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} —— 获取根元素的引用,以便将其传递给 gsap.context(scope)。
- ✅ return () => ctx.revert() —— Svelte 的 onMount 可以返回一个清理函数;在此处调用 ctx.revert(),以便在组件销毁时执行清理。
限定选择器作用域
不要使用可能匹配当前组件外部元素的全局选择器。始终将 scope(容器元素或 ref)作为第二个参数传递给 gsap.context(callback, scope),以确保在回调内部运行的任何选择器都局限于该子树。
- ✅ gsap.context(() => { gsap.to(".box", ...) }, containerRef) ——
.box仅在containerRef内部搜索。 - ❌ 在组件中不使用 context scope 而运行 gsap.to(".box", ...) 可能会影响其他实例或页面其余部分。
ScrollTrigger 清理
当你在补间动画/时间线上使用 scrollTrigger 配置或使用 ScrollTrigger.create() 时,会创建 ScrollTrigger 实例。它们被 包含 在 gsap.context() 中,并在调用 ctx.revert() 时被还原。因此:
- 将 ScrollTrigger 创建在用于补间动画的同一个 gsap.context() 回调中。
- 在布局发生变化后(例如数据加载后)调用 ScrollTrigger.refresh(),这些变化会影响触发器的位置;在 Vue/Svelte 中,这通常意味着在 DOM 更新之后(例如 Vue 中的 nextTick、Svelte 中的 tick,或在异步内容加载之后)。
何时创建与销毁
| 生命周期 | 操作 |
|---|---|
| 挂载 | 在 gsap.context(scope) 内部创建补间动画和 ScrollTrigger。 |
| 卸载 / 销毁 | 调用 ctx.revert(),以便该上下文中的所有动画和 ScrollTrigger 被销毁,内联样式被还原。 |
不要在组件的 setup 中或在同步的顶层脚本中创建 GSAP 动画,这些脚本在根元素存在之前运行。等待 onMounted / onMount(或等效方法),以确保容器 ref 在 DOM 中。
不要这样做
- ❌ 在组件挂载之前创建补间动画或 ScrollTrigger(例如在 setup 中而不使用 onMounted);DOM 节点可能还不存在。
- ❌ 使用不带 scope 的选择器字符串(将容器作为第二个参数传递给 gsap.context()),以免选择器匹配到组件外部的元素。
- ❌ 跳过清理;始终在 onUnmounted / onMount 的返回中调用 ctx.revert(),以便在组件销毁时销毁动画和 ScrollTrigger。
- ❌ 在每次渲染都会运行的组件体内注册插件(虽然不会造成损害,但浪费性能);在应用级别注册一次即可。
了解更多
- gsap-react 技能:针对 React 的模式(useGSAP、contextSafe)。