关于 gsap-frameworks
gsap-frameworks 为 Vue、Nuxt、Svelte 等组件框架中的 GSAP 动画提供生命周期使用指导,面向编写或审查相关代码的开发者。内容强调组件内选择器作用域及卸载清理,并提供 Vue 与 Nuxt 示例。
主要功能
- 01在 DOM 挂载后创建动画
- 02通过 gsap.context 限定选择器范围
- 03卸载时撤销动画和 ScrollTrigger
- 04提供 Vue、Nuxt 示例项目
- 05按需延迟加载 Nuxt 插件
适用场景
- 01在 Vue 组件中添加 GSAP 动画
- 02为 Svelte 组件处理动画清理
- 03排查组件间选择器互相影响
- 04在 Nuxt 中延迟加载 GSAP 插件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ greensock/ gsap-skills --skill gsap-frameworks安装后在 Claude Code 中输入/gsap-frameworks确认已加载。
详细介绍
在 Vue、Svelte 和其他框架中使用 GSAP
何时使用此技能
在 Vue(或 Nuxt)、Svelte(或 SvelteKit)或其他使用生命周期(挂载/卸载)的组件框架中编写或审查 GSAP 代码时使用。对于 React,请使用 gsap-react(useGSAP hook、gsap.context())。
相关技能: 补间和时间轴请使用 gsap-core 和 gsap-timeline;基于滚动的动画请使用 gsap-scrolltrigger;React 请使用 gsap-react。
原则(适用于所有框架)
- 在组件的 DOM 可用后(例如 onMounted、onMount)创建补间和 ScrollTrigger。
- 在卸载时(或同等的清理阶段)销毁或还原它们,确保不会在已脱离的节点上运行,也不会发生泄漏。
- 将选择器限定在组件根元素内,这样
.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); // once per app, e.g. in 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) — 将容器引用(例如
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 的模式。
使用可复用的组合式函数注册 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];
// In order to dynamically load all the GSAP plugins
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;
// Resolves the module type for a given key, then picks the named export matching the key
// this allows to have the type definitions for autocomplete in your code editor
type PluginModule<K extends Plugins> = Awaited<ReturnType<PluginMap[K]>>;
type PluginExport<K extends Plugins> = PluginModule<K>[K & keyof PluginModule<K>];
export default function () {
// Register all the GSAP Plugins you want at this point
gsap.registerPlugin(ScrollTrigger);
/*
If you want to lazy load some of the plugins that are
not widely used in your app (for example in just a couple
of components or a single route), you can use this method
*/
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() 时还原。因此:
- 在用于补间的同一个 gsap.context() 回调内创建 ScrollTrigger。
- 在影响触发器位置的布局变化后(例如数据加载后)调用 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。
- ❌ 在每次渲染都会运行的组件主体中注册插件(这不会造成问题,只是浪费);请在应用级别注册一次。
了解更多
- React 特定模式(useGSAP、contextSafe)的 gsap-react 技能。