lenis-smooth-scroll について
WebページにLenisのスムーズスクロールを導入し、GSAP ScrollTriggerと同期するためのスキルです。単一のアニメーションドライバーの選択、動きを減らす設定への対応、動作検証を案内します。
主な機能
- 01Lenisのスムーズスクロール設定
- 02単一RAFドライバーの選択
- 03GSAP ScrollTriggerとの同期
- 04動きを減らす設定への対応
- 05Playwrightによる検証
活用シーン
- 01ランディングページへの物理ベースのスクロール追加
- 02固定・パララックス領域とLenisの同期
- 03動きを減らす設定でのスクロール確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rheinmir/ setup --skill lenis-smooth-scrollインストール後、Claude Code で/lenis-smooth-scrollを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/demo.htmllenis-demo.csslenis-demo.js詳細
スキル:lenis-smooth-scroll
Lenisをサイト全体の物理ベースのスクロール基盤として設定し、GSAP ScrollTriggerと正しく同期します。これは、2つのライブラリを組み合わせる際に最もよく間違えられる手順です。
WHAT
目的とコンテキスト
- 目的: TOÀN TRANGの物理ベースのスクロール基盤としてLenisを構築し、GSAP ScrollTriggerと正しく同期します(rafドライバーは1つ、位相ずれなし、タブに戻ったときもカクつきなし)。デフォルトでは
Rheinmir/uiux-assetのオリジナル版をそのまま使用します。 - トリガー(使用するタイミング):
- ブラウザーのデフォルトの硬くカクつくスクロールではなく、Apple/awwwards風の「重みがあり滑らか」なスクロール感が必要なページ。
- GSAP ScrollTrigger(パララックス、リビール、ピン留め)をすでに使用している、または使用する予定があり、LenisとScrollTriggerが互いに干渉しない(カクつき、トリガー位置のずれがない)ようにする必要があるページ。
- 背景のスムーズスクロールは不要で、軽いスクロールエフェクトをいくつか使うだけの場合は使用しないでください(
scroll-effectsを使用します。Vanillaで依存関係は0です)。 - 1つのセクションだけをピン留め/横スクロールさせる目的には使用しないでください(その場合は
gsap-scrolltrigger-pinを使用します)。ただし、両方のニーズがあるページでは、まずこのスキルに従ってLenisを初期化します。どちらもScrollTriggerを軸にするため、その上でgsap-scrolltrigger-pinが通常どおり動作します。- ユーザーが「lenis」「smooth scroll」「apple風の滑らかなスクロール」「physics-based scroll」または
/lenis-smooth-scrollと言った場合。
- ユーザーが「lenis」「smooth scroll」「apple風の滑らかなスクロール」「physics-based scroll」または
- 対象外: 1つのセクションだけのピン留め/横スクロール(→
gsap-scrolltrigger-pin)、依存関係0の単発スクロールエフェクト(→scroll-effects)、作者のオリジナルコードの書き換え。
メンタルモデル
オリジナル(Rheinmir/uiux-asset)→ ファイルをそのままコピー → 内容だけを変更 | 外部ライブラリを取得できない場合:assets/(Vanillaの簡易版)→ ADAPT。技術的なコア:reduced-motion? → Lenisを使用しない / new Lenis → ドライバー:2A 自前のraf(GSAPなし) XOR 2B gsap.ticker(GSAPあり)→ lenis.on('scroll', ScrollTrigger.update) → reveal/data-speedも同じドライバーに合わせる。
入力と出力の契約
| フィールド | 必須? | 意味 | |
|---|---|---|---|
| In | 対象のページ/プロジェクト | はい | ビルドステップ(npm)があるか、静的ページ(CDN)か |
| In | GSAP ScrollTriggerを使用するか? | はい | ドライバー2Aまたは2Bを決定 |
| In | 外部ライブラリを取得できるか? | はい | オリジナル版またはassets/を決定 |
| Out | Lenisでスクロールするページ | はい | ドライバーは1つのみ。gsap.ticker経由の場合は* 1000。lagSmoothing(0)。reduced-motionの分岐をそのまま残す |
| Out | 検証の証拠 | はい | playwright-verify:高速スクロールで位置が飛ばないこと、およびpage.emulateMediaを使ったreduced-motionの分岐 |
ルールと機能
- RULE-01 (MUST):インストール:
npm i lenis gsap(ビルドステップがあるプロジェクトで推奨)、またはassets/demo.htmlにあるように、静的ページ/簡易デモではCDN(unpkg.com/lenis、cdnjs.cloudflare.com/.../gsap)を使用します。古いパッケージ名の@studio-freight/lenis(現在はlenisに改名済み)は、プロジェクトが理由あって古いバージョンに固定されている場合を除き、使用しないでください。 - RULE-02 (MUST):よくある間違い #1:
gsap.ticker.lagSmoothing(0)を忘れると、通常のスクロールは滑らかでも、ユーザーがタブを切り替えて戻ったときに一度カクつきます。これは、GSAPによるラグ補正がLenis独自のスムージング機構に重なるためです(「メカニズム」の項を参照)。 - RULE-03 (MUST):よくある間違い #2:2A(自前の
requestAnimationFrame(raf))と2B(gsap.ticker.add)の両方を同時に実行すると、2つのrafループの位相がずれて、継続的にわずかなカクつきが発生します。ドライバーは1つだけ選択してください。GSAPがある場合は2B、ない場合は2Aを使用します。 - RULE-04 (MUST):
gsap.tickerからlenis.raf(time)を呼び出す際、* 1000(秒→ミリ秒)を掛け忘れると、イージングの計算が誤ります。 - RULE-05 (MUST):
prefers-reduced-motion: reduceの場合、Lenisを初期化せず、代わりにCSSのscroll-behavior: smoothで疑似的に滑らかにもしないでください。ブラウザー本来のスクロールをそのまま使用します(assets/lenis-demo.jsに実装済みのため、適用時にこの分岐を削除しないでください)。 - RULE-06 (MUST):
playwright-verifyで検証します。連続して高速スクロールし、data-ls-revealとその要素を含むセクションの間で位置が飛ばないことを確認してください。reduced-motionの分岐もpage.emulateMediaでエミュレートしてテストします。 - 機能:外部リポジトリまたは
assets/からサンプルファイルを読み込み/コピーする、対象プロジェクトに書き込む、フロントエンド依存関係(lenis、gsap)をインストールする、ヘッドレスブラウザーを実行して検証する。
失敗時の境界
Rheinmir/uiux-assetを開く/クローンできない場合 → 簡略版の素のassets/を使用し(部分的)、ユーザーに伝えます。- ユーザーが
prefers-reduced-motion: reduceを有効にしている場合 → Lenis を初期化しません(正しい動作であり、エラーではありません)。 - 検証で位置のずれやカクつきが見つかった場合 → 失敗です。完了を報告する前に、よくある3つのエラー(#1~#3)を確認します。
- プロジェクトが理由があって
@studio-freight/lenisに固定している場合 → そのまま維持し、パッケージを変更する前にユーザーに確認します。
方法
メインワークフロー
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|---|---|---|---|---|
| W01 | judgment | 要件 | 「オリジナル」表から適切な元のペンを選び、リンクを開いて実際に動かし、エフェクトを確認します | 選択したペン | 外部ライブラリを導入できない → B01 |
| W02 | effect | ペン | .html ファイル全体(+_assets/)をプロジェクトにコピーし、クレジットコメントを残して、内容/画像/色だけを変更します。HTTP 経由で実行します | ページが動作する | — |
| W03 | deterministic | ページ | reduced-motion のガード後に Lenis をインストール(npm i lenis gsap または CDN)して初期化し、ドライバーを1つ選びます(2A または 2B) | Lenis が動作する | GSAP がある → B02 |
| W04 | deterministic | ページ | playwright-verify で検証します(高速スクロール+page.emulateMedia による reduced-motion) | pass | カクつき/位置のずれ → エラー #1~#3 を確認し、W03 を繰り返す |
各ステップの詳細(W01~W04 の信頼できる情報源):
オリジナル — 先に使用(ファイル全体をコピーし、書き直さない)
作者によるオリジナルコードの全文(公開 CodePen は MIT)は、外部リポジトリ Rheinmir/uiux-asset にあります。実際に動作するギャラリー:https://rheinmir.github.io/uiux-asset/scroll-effects/ 。
手順:リンクを開いて実際に動かし、エフェクトを確認 → .html ファイル全体(ファイルが参照している場合は _assets/ フォルダーも)をプロジェクトにコピー → 先頭行のクレジットコメントを残す → 内容/画像/色だけを変更します。file:// ではなく、HTTP 経由でローカル実行します。
オフラインで一式が必要な場合は、git clone --depth 1 https://github.com/Rheinmir/uiux-asset を実行します。
| 元のペン — 作者 | リンク |
|---|---|
| Lenis Smooth Scroll & GSAP Page — filipz | 実行 · コード |
| Lenis Smooth Scroll Cinematic Experience — filipz | 実行 · コード |
| Layout Explorations with GSAP, Flip, Lenis and ScrollTrigger N°2 — filipz | 実行 · コード |
| Staggered Text Scroll Reveal — gusevdigital | 実行 · コード |
このスキルの assets/ は、独自に作成した簡略版の素の実装です。プロジェクトに外部ライブラリを導入できない場合に使用します。通常は上記のオリジナルを使用します。
アセット
assets/demo.html + assets/lenis-demo.css + assets/lenis-demo.js — 実行可能なデモページ(CDN の Lenis + GSAP)です。読み込んでからプロジェクトに合わせて調整します(プロジェクトに応じてトークン/セクションを変更し、プロジェクトにビルド手順がすでにある場合は CDN を npm バンドルに変更します)。
仕組み
1. Lenis の初期化
const lenis = new Lenis({
lerp: 0.1, // nội suy mỗi frame (0–1): thấp = mượt/trễ hơn, cao = bám sát input hơn
duration: 1.2, // easing khi có target (vd anchor scroll), không áp dụng cho wheel liên tục
smoothWheel: true, // bật vật lý mượt cho wheel/trackpad — lý do chính để dùng Lenis
});
2A. 単独で使用(GSAP なし)— raf のループを独自に管理
function raf(time) {
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
2B. GSAP ScrollTrigger を使用する場合 — ドライバーを1つに統合する(2Aと2Bを同時に使用しない)
gsap.registerPlugin(ScrollTrigger);
lenis.on('scroll', ScrollTrigger.update); // Lenis cuộn -> báo ScrollTrigger cập nhật ngay
gsap.ticker.add((time) => lenis.raf(time * 1000)); // gsap.ticker LÀM vòng raf cho Lenis luôn
gsap.ticker.lagSmoothing(0); // tắt bù-lag mặc định của GSAP (xem lý do bên dưới)
この3行を正しい順序で記述する必要がある理由(最も間違いやすいポイント):
gsap.tickerは、すべての tween/ScrollTrigger を駆動するための内部requestAnimationFrameループをすでに実行しています。GSAP がある状態で Lenis が独自のrequestAnimationFrame(raf)(2A の方法)も実行すると、2つのループのタイミングが一致せず、フレームごとに数ミリ秒ずれて更新されます。その結果、ScrollTrigger が Lenis より1フレーム古い、または新しいスクロール位置を読み取ります。高速スクロール時、特にpinまたはscrubを使うセクションで、常にわずかなカクつきが感じられます。gsap.ticker を唯一のドライバーにするため、独立したrequestAnimationFrame(raf)を完全に削除し、gsap.ticker.add(...)に置き換えてください。gsap.tickerが出力するtimeは秒単位ですが、lenis.raf()はミリ秒を必要とするため、* 1000を乗算する必要があります。乗算を忘れてもエラーは出ませんが、Lenis の easing/velocity の計算が1000分の1の時間単位で行われるため、lerp の値によってスクロールが「止まった」ように見えたり、「飛ぶ」ように見えたりします。lenis.on('scroll', ScrollTrigger.update)は必須です。Lenis は内部の transform を通じてスクロール位置を更新するためです(各フレームでlerpし、scrollTopは即時更新されません)。通知しないと、ScrollTrigger はブラウザー本来のscrollイベントを使って pin/progress を計算し続けますが、その位置は Lenis が描画する「スムーズな」位置と一致しません。その結果、pin が数 px ずれたり、trigger が誤ったタイミングで発火したりします。gsap.ticker.lagSmoothing(0)は、見落としやすい別の問題に対処します。GSAP はデフォルトで、異常に遅れたフレーム(たとえば、タブが数秒間フォーカスを失ってから戻った場合)を検出し、アニメーションが一気に進まないように時間を「圧縮」して補正します。しかし Lenis にも独自の delta-time 平滑化機構があり、2つの時間補正機構が重なると、タブに戻ったときに滑らかになるどころか、はっきりとしたカクつきが1回発生します。GSAP の補正を完全に無効化(lagSmoothing(0))し、その処理を Lenis のみに任せてください。
3. prefers-reduced-motion: reduce を尊重する
動きを減らす設定を有効にしているユーザーに「擬似的なスムーズスクロール」を適用してはいけません。Lenis を初期化せず、ブラウザー標準のスクロール(通常のジャンプスクロール)を使います。new Lenis(...) を呼び出す前に確認してください:
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
// new Lenis(...) + sync ở trên
} else {
// cuộn gốc trình duyệt; nếu có reveal-on-scroll đi kèm, hiện thẳng bằng CSS/class, không chờ cuộn mượt
}
reveal のフォールバックデモを含む適用方法全体については、assets/lenis-demo.js を参照してください。
4. デモ:Lenis でスクロールする reveal と data-speed
assets/demo.html には、data-ls-reveal(段階的に表示される演出。手順2Bで同期したスクロールに連動する ScrollTrigger.create({ once: true }) を使用)と data-speed="0.12"(gsap.to(..., { scrollTrigger: { scrub: true } }) を通じてレイヤーをわずかに異なる速度で移動させる。動きに酔わないよう値を |v| ≤ 0.3 に制限)を指定したセクションがいくつかあります。どちらも、設定済みの Lenis/GSAP ドライバーを共用し、独立した scroll リスナーは作成しません。
分岐
| ID | 種類 | 条件 | 動作 | スキップ/失敗 | 再合流 |
|---|---|---|---|---|---|
| B01 | conditional_required | プロジェクトで外部ライブラリを追加できない/独自実装が必要 | assets/demo.html + assets/lenis-demo.css + assets/lenis-demo.js を使用し、トークン/セクションに合わせて調整 | — | W03 |
| B02 | conditional_required | ページで GSAP ScrollTrigger を使用している | ドライバー2B:lenis.on('scroll', ScrollTrigger.update) + gsap.ticker.add((time) => lenis.raf(time * 1000)) + gsap.ticker.lagSmoothing(0)。2A は完全に削除 | GSAP を使用しない → 2A | W04 |
| B03 | conditional_required | prefers-reduced-motion: reduce | new Lenis を使用せず、ブラウザー標準のスクロールを使用。reveal は CSS/class で最初から表示 | — | W04 |
| B04 | user_optional | pin/横スクロールのセクションを追加する必要がある | このスキルに従って先に Lenis を初期化し、その後、上記の gsap-scrolltrigger-pin を使用 | — | W04 |
検証と完了条件
playwright-verify で連続して高速スクロールしても、data-ls-reveal とそれを含むセクションの位置がずれず、page.emulateMedia で reduced-motion 分岐を確認したときに、Lenis を使わずブラウザー標準のスクロールになること。コード内に raf ドライバーが1つだけあること。
例
- 成功例:「Apple風のパララックス付きで、スクロールが滑らかなランディングページを作って」→ pen「Lenis Smooth Scroll & GSAP Page — filipz」を選び、
lenis-smooth-scroll-gsap-page.htmlをそのままコピーして、内容や画像を差し替えます → 高速スクロール時にピンがずれないこと、reduced-motionでは標準のスクロールになることを確認します。 - 境界例/失敗例: 適応したコードに
requestAnimationFrame(raf)とgsap.ticker.add(...)の両方が含まれている → わずかなカクつきが継続します(エラー #2)→ 2Aのループを削除し、2Bだけを残して再確認します。* 1000を忘れると、スクロールが「固まる/飛ぶ」状態になります(エラー #3)。
起源
Batch 180926 — 原文はRheinmir/uiux-asset/scroll-effects/にそのまま保存されています(上記の「原文」を参照)。assets/は自作の簡易版 vanilla です。