gsap-frameworks について
Vue、Nuxt、Svelteなど、ライフサイクルを持つコンポーネントフレームワークでGSAPアニメーションを実装・レビューするスキルです。DOM準備後の作成、セレクターのスコープ設定、クリーンアップを扱い、Reactではgsap-reactを案内します。
主な機能
- 01マウント後にアニメーションを作成
- 02セレクターをコンポーネント内に限定
- 03アンマウント時にアニメーションを復元
- 04ScrollTriggerインスタンスをクリーンアップ
- 05Nuxtプラグインを遅延ロード
活用シーン
- 01VueコンポーネントへのGSAPアニメーション追加
- 02ライフサイクルに沿ったSvelteアニメーションの実装
- 03コンポーネント破棄時のScrollTrigger管理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ nexu-io/ open-design --skill gsap-frameworksインストール後、Claude Code で/gsap-frameworksを入力して読み込まれたか確認します。
同梱ファイルLICENSESOURCES.md
詳細
Vue、Svelte、その他のフレームワークでの GSAP
GreenSock の公式 GSAP スキルをもとに厳選:https://github.com/greensock/gsap-skills
このスキルを使う場面
Vue(または Nuxt)、Svelte(または SvelteKit)、あるいはライフサイクル(マウント/アンマウント)を使用するその他のコンポーネントフレームワークで GSAP コードを作成またはレビューする際に適用してください。React については、gsap-react(useGSAP フック、gsap.context())を使用してください。
関連スキル: トゥイーンとタイムラインには gsap-core と gsap-timeline、スクロールベースのアニメーションには gsap-scrolltrigger、React には gsap-react を使用してください。
原則(すべてのフレームワーク)
- コンポーネントの DOM が利用可能になった後(例:onMounted、onMount)にトゥイーンと ScrollTrigger を作成します。
- 切り離されたノード上で処理が実行されたり、メモリリークが発生したりしないよう、アンマウント(または相当するタイミング)のクリーンアップでそれらを停止または元に戻します。
- セレクターをコンポーネントのルートにスコープし、
.boxなどがページのほかの部分ではなく、そのコンポーネント内の要素だけに一致するようにします。
Vue 3(Composition API)
これらのパターンを実演する、実行可能な Vite + Vue 3 プロジェクトについては、上流のサンプル https://github.com/greensock/gsap-skills/tree/aed9cfd3277740755f6bfc1155c7aa645403b760/examples/vue を参照してください。
コンポーネントが DOM に追加された後に GSAP を実行するには、onMounted を使用します。クリーンアップには 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)を第 2 引数として渡すと、.itemのようなセレクターがそのルート内に限定されます。コールバック内で作成されたすべてのアニメーションと ScrollTrigger が追跡され、ctx.revert() の呼び出し時に元に戻されます。 - ✅ onUnmounted — トゥイーンと ScrollTrigger を停止し、インラインスタイルを元に戻すため、必ず ctx.revert() を呼び出します。
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 など)は遅延読み込みし、初期バンドルサイズを削減します。
- ✅ Vue 3 と同様に、コンポーネントで gsap.context(scope) と onUnmounted → ctx.revert() を使用します。
Svelte
DOM の準備ができた後に GSAP を実行するには、onMount を使用します。元に戻すには、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() を呼び出します。
セレクターのスコープ設定
現在のコンポーネント外の要素にも一致する可能性のあるグローバルセレクターは使用しないでください。gsap.context(callback, scope) の第 2 引数には必ずスコープ(コンテナ要素または ref)を渡し、コールバック内で実行されるセレクターがそのサブツリー内に限定されるようにします。
- ✅ gsap.context(() => { gsap.to(".box", ...) }, containerRef) —
.boxはcontainerRef内だけで検索されます。 - ❌ コンポーネント内でスコープを指定せずに 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 アニメーションを作成しないでください。コンテナの ref が DOM 内に存在するよう、onMounted/onMount(または同等の処理)を待ってください。
避けること
- ❌ コンポーネントのマウント前にトゥイーンや ScrollTrigger を作成しないでください(例:onMounted を使わずに setup 内で作成する)。DOM ノードがまだ存在しない可能性があります。
- ❌ scope を指定せずにセレクター文字列を使用しないでください(gsap.context() の第 2 引数にコンテナを渡します)。指定しないと、セレクターがコンポーネント外の要素にも一致する可能性があります。
- ❌ クリーンアップを省略しないでください。コンポーネントの破棄時にアニメーションと ScrollTrigger が破棄されるよう、必ず onUnmounted 内または onMount の戻り値で ctx.revert() を呼び出してください。
- ❌ レンダリングのたびに実行されるコンポーネント本体内でプラグインを登録しないでください(問題はありませんが、無駄になります)。アプリレベルで一度だけ登録してください。
詳細はこちら
- React 固有のパターン(useGSAP、contextSafe)については gsap-react スキルをご覧ください。