Vue、Svelte、およびその他のフレームワークでのGSAP
このスキルを使用するタイミング
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)
実行可能なVite + Vue 3プロジェクトの例については、examples/vue/ を参照してください。
onMounted を使用して、コンポーネントがDOMに配置された後にGSAPを実行します。onUnmounted を使用してクリーンアップします。
import { onMounted, onUnmounted, ref } from "vue";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger); // アプリごとに1回(例: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 — 常に ctx.revert() を呼び出し、トゥイーンとScrollTriggerを強制終了し、インラインスタイルを元に戻します。
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プロジェクトについては、
examples/nuxt/を参照してください。
再利用可能なコンポーザブル を使用して、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など)を遅延ロードし、初期バンドルサイズを削減します。
- ✅ コンポーネント内では、Vue 3と同様に gsap.context(scope) と onUnmounted → ctx.revert() を使用します。
Svelte
onMount を使用して、DOMの準備ができた後にGSAPを実行します。onMountの返されたクリーンアップ関数(またはコンテキストを追跡してリアクティブブロック/コンポーネント破棄時にクリーンアップ)を使用して元に戻します。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() を呼び出し、コンポーネントが破棄されるときにクリーンアップが実行されるようにします。
セレクターのスコープ
現在のコンポーネントの外部の要素にマッチする可能性のあるグローバルセレクターを使用しないでください。常にスコープ(コンテナ要素またはref)を gsap.context(callback, scope) の第2引数として渡し、コールバック内で実行されるセレクターがそのサブツリーに制限されるようにします。
- ✅ gsap.context(() => { gsap.to(".box", ...) }, containerRef) —
.boxはcontainerRef内でのみ検索されます。 - ❌ コンポーネント内でコンテキストスコープなしに gsap.to(".box", ...) を実行すると、他のインスタンスやページの残りの部分に影響を与える可能性があります。
ScrollTriggerのクリーンアップ
ScrollTriggerインスタンスは、トゥイーン/タイムラインに scrollTrigger 設定を使用するか、ScrollTrigger.create() を使用するときに作成されます。これらは 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ノードがまだ存在しない可能性があります。
- ❌ スコープなしでセレクター文字列を使用しないでください(コンテナをgsap.context()の第2引数として渡します)。セレクターがコンポーネント外の要素にマッチするのを防ぎます。
- ❌ クリーンアップをスキップしないでください。常に ctx.revert() をonUnmounted / onMountの戻り値で呼び出し、コンポーネントが破棄されるときにアニメーションとScrollTriggerを強制終了します。
- ❌ レンダリングのたびに実行されるコンポーネント本体内でプラグインを登録しないでください(害はありませんが、無駄です)。アプリレベルで1回登録してください。
詳細
- gsap-react スキル:React固有のパターン(useGSAP、contextSafe)についてはこちらを参照してください。