gsap-web について
gsap-webは、GSAPを使うウェブ開発者向けのアニメーションスキルです。タイムライン、スクロール演出、テキスト表示、レイアウト遷移に加え、単体HTMLの検証手順も扱います。
主な機能
- 01GSAPタイムラインと精密なシーケンス
- 02ScrollTriggerによるスクロール演出
- 03SplitTextによるテキスト表示
- 04Flipによるレイアウト遷移
- 05アニメーションのクリーンアップと性能指針
- 06単体HTMLのスクリーンショット検証
活用シーン
- 01スクロール連動のウェブ演出を作る
- 02複数要素のヒーロー演出を組む
- 03テキスト表示やレイアウト変更を作る
- 04スムーススクロールとScrollTriggerの同期を調べる
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ iart-ai/ web-animation-skills --skill gsap-webインストール後、Claude Code で/gsap-webを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdexamples/hero-timeline.jsstandalone-template.htmlreferences/scrolltrigger-cookbook.mdscrolltrigger-lenis.md詳細
Web 向け GSAP
GSAP(GreenSock Animation Platform)は、シーケンス化されたタイムライン、スクロール連動のストーリーテリング、テキストの表示、レイアウト遷移など、コードで制御する Web アニメーションの主力ツールです。GSAP 3.12 以降では、すべてのプラグイン(ScrollTrigger、SplitText、Flip、MotionPath、MorphSVG、Draggable、Observer)が、商用利用を含めて完全に無料です。
使用する場面
- スクロール連動のストーリーテリング:ピン留めされたセクション、パララックス、進行状況のスクラブ、横スクロール
- 正確な重なり具合を制御する、シーケンス化されたヒーローアニメーションや複雑な複数要素のタイムライン
- 文字・単語・行に分割したテキストの表示(SplitText)
- 要素の位置・サイズ・親要素が変化するレイアウト遷移(Flip)
- 細かなタイミング制御、イージングの精度、または命令型のオーケストレーションが必要なあらゆるモーション
インストールと登録
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { SplitText } from "gsap/SplitText";
import { Flip } from "gsap/Flip";
gsap.registerPlugin(ScrollTrigger, SplitText, Flip);
プラグインは使用前に必ず登録してください。登録しない場合、何も起こらずに処理が無視されます。バンドラーを使う場合は、アプリのエントリーポイントで一度だけ登録してください。
コアテクニック
まずタイムラインを使う
独立した複数のトゥイーンよりも、1つのタイムラインを優先してください。単一の再生ヘッド、相対的な配置、簡単な逆再生が可能になります。
const tl = gsap.timeline({ defaults: { ease: "power3.out", duration: 0.6 } });
tl.from(".title", { yPercent: 100, opacity: 0 })
.from(".sub", { y: 20, opacity: 0 }, "-=0.3") // start 0.3s before prev ends
.from(".cta", { scale: 0.9, opacity: 0 }, "<"); // align to prev tween START
位置パラメーターの早見表:
"+=0.5"/"-=0.3"— タイムラインの終端を基準とする(間隔/重なり)"<"— 直前のトゥイーンの開始位置、">"— 直前のトゥイーンの終了位置"<0.2"— 直前のトゥイーンの開始から0.2秒後"myLabel"—tl.addLabel("myLabel")で追加した名前付きラベルの位置
開始時のアニメーションには .from() を使います(指定した値から現在の CSS 値へアニメーションします)。終了時には .to() を使います。両端を明示する必要がある場合は .fromTo() を使います(再実行時にも最も確実です)。
gsap.to と set と quickTo
gsap.set(el, { autoAlpha: 0 }); // instant, no tween (autoAlpha = opacity + visibility)
const xTo = gsap.quickTo(el, "x", { duration: 0.4, ease: "power3" });
window.addEventListener("pointermove", (e) => xTo(e.clientX)); // fast repeated updates
autoAlpha は、生の opacity よりも推奨されます。値が0のときに visibility:hidden も切り替え、要素をヒットテストの対象から外すためです。
ScrollTrigger の基本
gsap.to(".panel", {
xPercent: -100,
ease: "none",
scrollTrigger: {
trigger: ".wrap",
start: "top top", // when trigger top hits viewport top
end: "+=2000", // 2000px of scroll distance
pin: true, // freeze .wrap while the tween plays
scrub: 1, // tie progress to scrollbar (1 = 1s catch-up smoothing)
markers: true, // dev-only visual markers — remove for prod
},
});
主な仕様:
start/endには"triggerPos viewportPos"(例:"top center")または"+=px"を指定します。scrub: trueはアニメーションの進行度をスクロールに正確に同期させます。scrub: <number>は遅延を加えて滑らかにします。toggleActions: "play pause resume reverse"は、スクラブを使わないトリガーにおける onEnter/onLeave/onEnterBack/onLeaveBack の動作を制御します。- スクロールに連動するトゥイーンでは、モーションがスクロールに線形に追従するよう
ease: "none"を使います。
スムーススクロール(Lenis)+ ScrollTrigger
Lenis(または Locomotive)のスムーススクロールを ScrollTrigger と組み合わせると、両方を1つのループで動かさない限り同期がずれます。Lenis は独自の rAF でスクロールを滑らかにし、ScrollTrigger は GSAP のティッカーでスクロール位置を読み取ります。それぞれが独立して動くと、ScrollTrigger は古い位置を読み取ります。解決策は、GSAP のティッカーから Lenis を動かし、Lenis のスクロールごとに ScrollTrigger を更新することです。
import Lenis from "lenis";
const lenis = new Lenis({ duration: 1.2, smoothWheel: true });
lenis.on("scroll", ScrollTrigger.update); // 1. update ScrollTrigger on every Lenis scroll
gsap.ticker.add((t) => lenis.raf(t * 1000)); // 2. one loop: ticker drives Lenis (seconds → ms)
gsap.ticker.lagSmoothing(0); // 3. stop GSAP catch-up jitter on heavy frames
重要:gsap.ticker が渡す時間の単位は秒ですが、lenis.raf() が必要とする単位はミリ秒です。1000を掛けてください。Lenis 用に独立した requestAnimationFrame(raf) ループも実行しないでください。二重に動作してしまいます。
よくある不具合:
- 揺れ・カクつき — 残った
requestAnimationFrame(raf)ループがティッカーと競合しているか、lagSmoothing(0)が設定されていません。余計なループを削除し、ラグスムージングを0に設定してください。 - マーカーがトリガーからずれる —
ScrollTrigger.updateがlenis.on("scroll", …)に登録されていません。 - ピン留めが機能しない — Lenis 使用時は、
scrollerProxyを設定しないでください(ドキュメントをスクロールします)。Locomotive 使用時は、scrollerProxyとpinTypeを必ず連携させ、すべてのトリガーにscroller:を設定してください。 - スクロールが止まる/方向が反転する —
* 1000の単位変換がないか、2つのループの実行順序が逆になっています。
SPA のアンマウント時には、gsap.ticker.remove(update) と lenis.destroy() でクリーンアップしてください。そうしないと、ナビゲーションのたびにループが積み重なります。prefers-reduced-motion に応じて制御し、該当する場合は Lenis をスキップしてネイティブスクロールを使ってください。Lenis と Locomotive の完全な連携方法、アンカーリンクのルーティング、data-lenis-prevent、React(useGSAP/useEffect)、および症状→原因の一覧表については、references/scrolltrigger-lenis.md を参照してください。
SplitText(テキストの表示)
const split = SplitText.create(".headline", { type: "lines, words", linesClass: "line" });
gsap.from(split.lines, { yPercent: 100, opacity: 0, stagger: 0.08, duration: 0.7, ease: "power4.out" });
注意点:
- 行マスクで表示する場合は、行のラッパーに
overflow: hiddenを設定して、yPercent: 100で確実に隠れるようにします。フォントの読み込み時やリサイズ時に再分割するには、autoSplit: true(GSAP 3.13+)を追加します。 - 再分割する前、またはアンマウント時には
split.revert()を呼び出し、元の DOM を復元してノードの重複を防ぎます。 - 改行位置の誤りを防ぐため、必ず Web フォントの読み込み後に分割します(
document.fonts.ready.then(...))。
Flip(レイアウト遷移)
Flip は状態を記録し、DOM を即座に変更した後、見た目の差分をアニメーション化します(FLIP テクニック)。グリッドとリストの切り替え、カードの展開、親要素の変更に最適です。
const state = Flip.getState(".item"); // 1. capture BEFORE
container.classList.toggle("grid"); // 2. mutate DOM/CSS (instant)
Flip.from(state, { // 3. animate the delta
duration: 0.6, ease: "power2.inOut", stagger: 0.05,
absolute: true, // take items out of flow during move (prevents reflow jitter)
});
イージング早見ガイド
power2/3.out— 登場時(速く始まり、落ち着きます)power2.inOut— 画面上の 2 つの状態間を移動しますback.out(1.7)— オーバーシュート/ポップ(数値はオーバーシュート量)elastic.out(1, 0.3)— 弾む動きです。使いすぎないようにしますnone— スクロールに連動するトゥイーンsteps(n)— スプライト/段階的な動き- カスタム cubic-bezier 相当:
CustomEase.create("x", "M0,0 C0.2,0 0,1 1,1")(CustomEase は無料です)
パフォーマンスとクリーンアップ
transform(x/y/xPercent/scale/rotation)とopacityのみをアニメーション化します。これらは GPU で合成され、レイアウト/ペイントを省略できます。top/left/width/heightのアニメーション化は避けます。- 負荷の高い要素や固定表示する要素には
will-change: transformを設定し、処理後に削除します。 - 多数の類似したスクロール表示は、要素ごとにトリガーを設定する代わりに
ScrollTrigger.batch()でまとめます。 - ビューポートやコンテンツの変更後は、
ScrollTrigger.refresh()を呼び出します。
SPA/フレームワークではクリーンアップが必須です。孤立したトリガーはメモリーリークや意図しないピン留めの原因になります。gsap.context()(または @gsap/react の useGSAP)を使用します。
useEffect(() => {
const ctx = gsap.context(() => {
// all gsap + ScrollTrigger code here
}, rootRef);
return () => ctx.revert(); // kills tweens, triggers, and reverts inline styles
}, []);
提出と検証(スタンドアロン HTML)
同梱ヘルパー(
scripts/):scripts/seek-shot.sh anim.html 0 1.5 3は?t=Nハーネスで再生を停止し、各時点のスクリーンショットを撮影します。scripts/contact-sheet.sh sheet.png frame-*.pngは画像を並べて、一目で確認できる一覧を作成します。scripts/README.mdを参照してください。
自己完結型のアニメーション(ヒーロー、表示アニメーション、ループ、マイクロシーン)では、納品物はブラウザーで直接開ける 1 つの HTML ファイルです。ビルド手順、フレームワーク、レンダリングパイプラインは必要ありません。作業の規模に合った納品物を選びます。Web モーションには単一ファイルが適切です。1 つのファイルで済むときに、バンドラーを使う必要はありません。
出力要件:
.htmlファイルを 1 つ用意します。GSAP とプラグインを CDN から読み込み、マークアップとアニメーションを 1 つのインライン<script>に含めます。- すべてのモーションを1 つのマスタータイムライン(
const tl = gsap.timeline())にまとめます。これにより、シーク可能な単一の再生ヘッドを使えます。 - 下記のシークハーネスを含め、任意の時点で停止して確認できるようにします。
シークハーネス — スクリーンショット用に正確な時点で停止します。 動画プレーヤーでフレームを固定するのと同様に、?t=N を指定するとマスタータイムラインが N 秒の位置にシークして一時停止し、決定的な静止フレームをスクリーンショットに収められます。
<script>
// ... build your master timeline as `tl` ...
const t = new URLSearchParams(location.search).get("t");
if (t !== null) { tl.pause(); tl.seek(parseFloat(t)); } // frozen at t seconds
// no ?t → plays normally
window.__ready = true; // ready signal for headless wait
console.log("duration", tl.duration());
</script>
検証手順 — レンダリング → 停止 → スクリーンショット → 確認:
- タイムラインの開始、中間、終了の 3 つの時点でファイルを開きます。
…/anim.html?t=0、?t=<dur/2>、?t=<dur>を指定します。終了時刻はコンソールでtl.duration()を確認します。 - 停止した各フレームのスクリーンショットを撮影します。
- 忠実度(要件に合っているか)とアーティファクト(テキストの切れ、キャンバス外の要素、フォント読み込み前の FOUC、継ぎ目でのカクつき)の両方を確認します。意図が明確で、完成度の高い出力にします。
ヘッドレススクリーンショットツールなら何でも使用できます。エージェントのブラウザーツール、または Playwright が使えます。
npx playwright screenshot --wait-for-timeout=500 "file://$PWD/anim.html?t=1.2" frame-mid.png
完了前に確認すること:
- ブラウザーでスタンドアロン実行できます。コンソールエラーや CDN の読み込み漏れがありません。
- すべてのアニメーションが 1 つのマスタータイムラインにあり、
?t=Nで正しく停止します。 - 開始/中間/終了のスクリーンショットを撮影し、要件に合っていて、アーティファクトがありません。
prefers-reduced-motionに対応しています(タイムラインを簡略化するか、スキップします)。- イージングに意図があり、空間的な動きに誤って
linearを使っていません。
ハーネスを組み込んだ、実行可能な完全版テンプレートは examples/standalone-template.html にあります。
クイックリファレンス
| 目的 | API |
|---|---|
| 重なりを持たせたシーケンス | tl.from(...).from(..., "-=0.3") |
| スクロール中に固定 | scrollTrigger:{ pin:true, end:"+=N" } |
| スクロールに合わせてスクラブ | scrollTrigger:{ scrub:1, ease:"none" } |
| テキストの行を表示 | SplitText.create(el,{type:"lines"}) |
| レイアウト変更をアニメーション化 | Flip.getState → 変更 → Flip.from |
| ポインターをすばやく追従 | gsap.quickTo(el,"x",{...}) |
| すべてを停止 | ScrollTrigger.getAll().forEach(t=>t.kill()) |
リファレンスファイル
references/scrolltrigger-cookbook.md— ピン留め、スクラブ、横スクロール、スナップ、ScrollTrigger.batch()による表示、視差効果、ネストされたトリガー、SPA のクリーンアップパターンを、コード全体とともに説明します。references/scrolltrigger-lenis.md— ScrollTrigger と Lenis/Locomotive のスムーススクロール同期を詳しく説明します。標準的な設定、scrollerProxy、アンカーリンク、動きを減らす設定、React(useGSAP/useEffect)、デバッグ用チェックリスト、症状→原因の一覧を含みます。examples/hero-timeline.js— SplitText、段階的な表示、動きを減らす設定への対応を含む、実行可能なヒーロー表示タイムラインの完全な例です。examples/standalone-template.html— 成果物のテンプレートです。自己完結型(CDN GSAP)、マスタータイムラインを1つ使用し、スクリーンショット検証用の?t=Nシーク機能と、動きを減らす設定への対応を備えています。