mcpField
G
gsap-frameworks

gsap-frameworks

github.com/nexu-io设计与 UI104.6k2.1k 次安装1 天前更新
代码仓库

关于 gsap-frameworks

用于在 Vue、Nuxt、Svelte 等具有生命周期的组件框架中编写和审查 GSAP 动画的技能,重点是 DOM 就绪后初始化、选择器作用域和清理;React 项目应使用 gsap-react。

主要功能

  1. 01挂载后创建动画
  2. 02将选择器限定在组件内
  3. 03卸载时还原动画
  4. 04清理 ScrollTrigger 实例
  5. 05按需加载 Nuxt 插件

适用场景

  1. 01为 Vue 组件添加 GSAP 动画
  2. 02编写生命周期安全的 Svelte 动画
  3. 03在组件销毁时管理 ScrollTrigger

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nexu-io/open-design --skill gsap-frameworks

安装后在 Claude Code 中输入/gsap-frameworks确认已加载。

附带文件LICENSESOURCES.md

详细介绍

GSAP 与 Vue、Svelte 及其他框架

整理自 GreenSock 官方 GSAP 技能:https://github.com/greensock/gsap-skills

何时使用此技能

在 Vue(或 Nuxt)、Svelte(或 SvelteKit)或其他使用生命周期(挂载/卸载)的组件框架中编写或审查 GSAP 代码时使用。对于 React,请使用 gsap-react(useGSAP hook、gsap.context())。

**相关技能:**补间动画和时间线请使用 gsap-core 和 gsap-timeline;基于滚动的动画请使用 gsap-scrolltrigger;React 请使用 gsap-react。

原则(适用于所有框架)

  • 在组件的 DOM 可用后(例如 onMounted、onMount)创建补间动画和 ScrollTriggers。
  • 在卸载(或等效阶段)时销毁或还原它们,确保不会在已脱离的节点上运行,也不会造成泄漏。
  • 将选择器限定在组件根元素内,这样 .box 等选择器只会匹配该组件中的元素,而不会匹配页面其他部分的元素。

Vue 3(Composition API)

如需查看演示这些模式的可运行 Vite + Vue 3 项目,请参阅上游示例:https://github.com/greensock/gsap-skills/tree/aed9cfd3277740755f6bfc1155c7aa645403b760/examples/vue。

使用 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) — 将容器 ref(例如 container.value)作为第二个参数传入,这样 .item 等选择器就会限定在该根元素内。回调中创建的所有动画和 ScrollTriggers 都会被追踪,并在调用 ctx.revert() 时还原。
  • ✅ onUnmounted — 始终调用 ctx.revert(),以销毁补间动画和 ScrollTriggers,并还原内联样式。

Vue 3(script setup)

在 <script setup> 中使用 ref,思路相同:

<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

如需查看包含插件注册、延迟加载和 SSR 安全模式的可运行 Nuxt 4 项目,请参阅 https://github.com/greensock/gsap-skills/tree/aed9cfd3277740755f6bfc1155c7aa645403b760/examples/nuxt。

使用可复用的组合式函数注册 GSAP 插件,并延迟加载应用中不常用的插件:

// composables/useGSAP.ts
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

// 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 LoadablePlugin = 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 LoadablePlugin> = Awaited<ReturnType<PluginMap[K]>>;
type PluginExport<K extends LoadablePlugin> = 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 LoadablePlugin>(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 作用域运行 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(或等效钩子),确保容器引用已在 DOM 中。

请勿

  • ❌ 在组件挂载之前创建补间动画或 ScrollTrigger(例如,在 setup 中不使用 onMounted);此时 DOM 节点可能尚不存在。
  • ❌ 不带 scope 使用选择器字符串(将容器作为第二个参数传给 gsap.context()),否则选择器可能会匹配组件外部的元素。
  • ❌ 跳过清理;始终在 onUnmounted 中调用 ctx.revert(),或在 onMount 的返回函数中调用,以便在组件销毁时销毁动画和 ScrollTrigger。
  • ❌ 在每次渲染都会运行的组件主体中注册插件(这不会造成问题,只是浪费);请在应用级别注册一次。
了解更多
  • 面向 React 特定模式(useGSAP、contextSafe)的 gsap-react 技能。

评论

支持 Markdown