Communitygithub.com

KILN-skills/scroll-animation

GSAP、ScrollTrigger、SplitText、Lenisを使用したスクロール駆動アニメーション(リビール、固定セクション、水平スクロール、パララックス、スクラブ付きタイムライン、テキストエフェクト)をVanilla JS、React、Next.js、Vue/Nuxt、Astroで構築し、正しいセットアップ、クリーンアップ、レスポンシブフォールバックを提供します。GSAP、ScrollTrigger、Lenis、スムーススクロール、パララックス、ピン、スクラブ、"アニメーション"、水平スクロール、スクロールストーリーテリングに関する言及があった場合に使用します。

scroll-animation とは?

このスキルは、GSAP、ScrollTrigger、SplitText、Lenisを使用したスクロール駆動アニメーションの構築を支援します。リビール、固定セクション、水平スクロール、パララックス、スクラブ付きタイムライン、テキストエフェクトなどの効果を、Vanilla JS、React、Next.js、Vue/Nuxt、Astroの各フレームワークで実装するための正しいセットアップ、クリーンアップ、レスポンシブフォールバックを提供します。このスキルは、GreenSockの公式スキル(gsap-core、gsap-scrolltrigger、gsap-react、gsap-plugins)がインストールされている場合、それらをAPIの詳細に使用することを前提とし、プロジェクトのセットアップ、統合、および実際のサイトで問題を引き起こす一般的なミスに焦点を当てています。また、パフォーマンスの最適化、アクセシビリティ(prefers-reduced-motion)、レスポンシブデザイン、およびSPAでのルート変更処理についてもカバーしています。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/KILN-skills/kiln/tree/HEAD/skills/scroll-animation

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

スクロールアニメーション (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。

関連スキル