スクロールアニメーション (GSAP + ScrollTrigger + Lenis)
GreenSockの公式スキル(gsap-core、gsap-scrolltrigger、gsap-react、gsap-plugins)がインストールされている場合は、それらをAPIの詳細に使用してください。このスキルは、プロジェクトのセットアップ、統合、および実際のサイトで問題を引き起こす一般的なミスについて説明します。
1. インストール
npm i gsap lenis
npm i @gsap/react # React / Next.js のみ
すべてのGSAPプラグイン(ScrollTrigger、SplitText、Flip、ScrollSmoother、MorphSVG、DrawSVG…)は公開gsapパッケージに含まれており、商用利用は無料です。gsap/<PluginName>からインポートし、gsap.registerPlugin(...)を一度呼び出します。
2. 1つのセットアップモジュール
クライアント側で一度だけインポートされるlib/motion/setup.(js|ts)(またはanimations/)を作成します。
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { SplitText } from "gsap/SplitText";
import Lenis from "lenis";
import "lenis/dist/lenis.css";
gsap.registerPlugin(ScrollTrigger, SplitText);
export function initSmoothScroll() {
if (matchMedia("(prefers-reduced-motion: reduce)").matches) return null;
const lenis = new Lenis();
lenis.on("scroll", ScrollTrigger.update);
const tick = (time) => lenis.raf(time * 1000); // GSAPの時間は秒、Lenisはミリ秒
gsap.ticker.add(tick);
gsap.ticker.lagSmoothing(0);
return () => { gsap.ticker.remove(tick); lenis.destroy(); };
}
Lenis または GSAP ScrollSmootherのいずれかを使用し、両方は使用しないでください。
3. フレームワーク統合
React / Next.js (App Router) — GSAPを使用するファイルには"use client"が必要です。常にuseGSAPを使用してください(自動クリーンアップ、Strict Modeでの二重実行防止)。
"use client";
import { useRef } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(useGSAP, ScrollTrigger);
export function Features() {
const root = useRef<HTMLElement>(null);
useGSAP(() => {
const mm = gsap.matchMedia();
mm.add("(prefers-reduced-motion: no-preference)", () => {
gsap.from(".feature", {
autoAlpha: 0, y: 32, stagger: 0.08, ease: "power3.out",
scrollTrigger: { trigger: root.current, start: "top 75%" },
});
});
}, { scope: root }); // セレクタはこのコンポーネントにスコープされます
return <section ref={root}>…</section>;
}
- アニメーションを作成するイベントハンドラ:
useGSAPのcontextSafeでラップします。 - スムーススクロールプロバイダ:ルートレイアウトのクライアントコンポーネントで、エフェクト内で
initSmoothScroll()を呼び出すか(またはautoRaf: falseとGSAPのtickerを指定したlenis/reactのReactLenisを使用)、initSmoothScroll()を呼び出します。 - Next.jsでのルート変更:上部にスクロール(
lenis.scrollTo(0, { immediate: true }))し、新しいページがマウントされた後にScrollTrigger.refresh()を呼び出します。
Vue / Nuxt — onMounted内のgsap.context(() => {…}, rootEl)で作成し、onUnmountedでctx.revert()を呼び出します。Nuxt:クライアント専用プラグイン(plugins/gsap.client.ts)。
Astro / 静的マルチページ — レイアウト内の<script>。View Transitionsを使用する場合、astro:page-loadで再初期化し、astro:before-swapでトリガーを削除します。
Vanilla — DOM準備完了後に実行される1つのmain.js。gsap.context()内でセクションごとにグループ化します。
4. バグの90%を防ぐScrollTriggerルール
scrollTriggerはタイムラインまたはトップレベルのトゥイーンに配置します。タイムライン内にネストされたトゥイーンには決して配置しないでください。scrubを使用する場合はease: "none"を使用します。- 作成順序が重要です:トリガーはページ順に上から下に作成します(または
refreshPriorityを設定します)。特にピン留めの場合。 - ラッパーをピン留めし、アニメーションさせる要素自体はピン留めしないでください。
transformされた親要素内でピン留めしないでください。意図的でない限り、pinSpacing: falseは避けてください。 - ビューポートサイズに依存するものには関数値(
x: () => …)+invalidateOnRefresh: trueを使用します。 - 画像/フォント/動的コンテンツがレイアウトを変更した後に
ScrollTrigger.refresh()を呼び出します(window.addEventListener("load", …)、document.fonts.ready)。 markers: trueでデバッグします。出荷前に削除してください。- 初期の非表示状態:
gsap.from/fromToまたはJSで設定したautoAlphaを使用します。これにより、JSなしでもコンテンツが表示されます。 - ほとんどのリビールでは、再再生を防ぐために
once: trueを使用します。
5. レスポンシブ & モーション軽減
すべてのシーンをgsap.matchMedia()でラップします。
const mm = gsap.matchMedia();
mm.add({
isDesktop: "(min-width: 1024px)",
isMobile: "(max-width: 1023px)",
motionOK: "(prefers-reduced-motion: no-preference)",
}, ({ conditions: c }) => {
if (!c.motionOK) return; // 静的なレイアウト、アニメーションなし
if (c.isDesktop) { /* ピン留め / 水平シーン */ }
if (c.isMobile) { /* シンプルなフェードアップのみ */ }
});
matchMediaは条件が変更されると自動的に元に戻します。手動でのクリーンアップは不要です。
6. パフォーマンス
x/y/scale/rotation/autoAlpha(トランスフォームと不透明度)をアニメーションさせます。width/height/top/leftや、広い領域に対する重いfilter: blur()のアニメーションは避けてください。- 多数の個別トリガーよりも、セクションごとに1つのタイムラインを優先します。多くの同一リビールには
ScrollTrigger.batchを使用します。 - ポインタ追従エフェクトには
gsap.quickToを使用します。 - スクラブシーン内の大きな画像:適切な
sizesを使用し、毎フレーム巨大なビットマップをスケーリングしないようにします。
7. 確認
ページ全体を上下に高速スクロールし、ウィンドウをリサイズし、ページ途中でリロードし、移動して戻ってきます(SPA)。ジャンプ、ピンの重複、コンソールエラーがないことを確認します。モーション軽減オン、375px、CPUスロットリングありでテストします。
一般的なエフェクトのレシピ:../motion-direction/references/patterns.md。