mcpField
G
gsap-frameworks

gsap-frameworks

github.com/greensock设计与 UI17.4k64.1k 次安装27 周前更新
代码仓库

关于 gsap-frameworks

gsap-frameworks 为 Vue、Nuxt、Svelte 等组件框架中的 GSAP 动画提供生命周期使用指导,面向编写或审查相关代码的开发者。内容强调组件内选择器作用域及卸载清理,并提供 Vue 与 Nuxt 示例。

主要功能

  1. 01在 DOM 挂载后创建动画
  2. 02通过 gsap.context 限定选择器范围
  3. 03卸载时撤销动画和 ScrollTrigger
  4. 04提供 Vue、Nuxt 示例项目
  5. 05按需延迟加载 Nuxt 插件

适用场景

  1. 01在 Vue 组件中添加 GSAP 动画
  2. 02为 Svelte 组件处理动画清理
  3. 03排查组件间选择器互相影响
  4. 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 技能。

评论

支持 Markdown