SAMPLE
慣性スクロール(Lenis)

INDEX
説明
ページスクロール時の動きを滑らかにし、スクロール後の動きに余韻をもたせる方法について解説します。
使用するライブラリ
※執筆時点でVer. 1.2.3の情報です。
サンプル

手順
1. インストール
まずはLenisをプロジェクト上にインストールします。
npm i lenis
npmなどのパッケージマネージャーを使用しない場合はCDNでの読み込みも可能です。
<script src="https://unpkg.com/[email protected]/dist/lenis.min.js"></script>
2. CSSの追加
公式が準備しているCSSファイルをプロジェクトに追加します。
@import 'lenis/dist/lenis.css';
npmなどのパッケージマネージャーを使用しない場合はCDNでの読み込みも可能です。
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/lenis.css">
3. セットアップ
JavaScriptでLenisインスタンスの作成を行います。
初期化時にはrequestAnimationFrame
を使用してスクロールアニメーションの処理を再帰的に呼び出す必要があります。
なお、呼び出し方法については以下の3パターンがあります。
例1. autoRafオプションを使用する場合
// 初期化
const lenis = new Lenis({
autoRaf: true, // requestAnimationFrameでスクロール処理を継続的に更新するかどうか
});
例2. rafメソッドを呼び出す場合
autoRaf
オプションを使用せずにrequestAnimationFrame
内でスクロールアニメーションの処理を直接呼び出す方法もあります。
// 初期化
const lenis = new Lenis()
// requestAnimationFrameでスクロール処理を継続的に更新
function raf(time) {
lenis.raf(time)
requestAnimationFrame(raf)
}
requestAnimationFrame(raf)
例3. GSAP ScrollTriggerと統合する場合
プロジェクト上でGSAP ScrollTriggerを使用している場合、再帰呼び出し時にGSAPアニメーションの処理とLenisの処理が衝突する懸念があります。
よって、GSAPとLenisの処理を統合するには、GSAP独自のrequestAnimationFrame
システム(gsap.ticker
)上でLenisの処理を呼び出すことが推奨されています。
また、GSAPではレンダリング時に、ある一定時間のラグ(デフォルトで500ミリ秒)が発生した際にアニメーションを自動的に抑制する機能が含まれていますが、
これによりLenisのスクロール処理に遅延が発生する懸念があるため、この機能も合わせて無効化しておきます。
// 初期化
const lenis = new Lenis();
// Lenisのスクロールイベントに合わせて全てのScrollTriggerインスタンスをアップデート
lenis.on('scroll', ScrollTrigger.update);
// LenisのrafメソッドをGSAP tickerに追加
gsap.ticker.add((time) => {
lenis.raf(time * 1000); // tickerのタイムスタンプの単位は秒なので、ミリ秒に変換
});
// レンダリング時にラグが発生した際のアニメーション調整機能を無効化
gsap.ticker.lagSmoothing(0);
手順3までの処理で基本的な設定は完了です🎉
ページ上で慣性スクロールが適用されていることを確認してみてください。
設定オプション
慣性スクロールの適用対象や滑らかさなどの設定は、インスタンス生成時に特定のオプションを渡すことで調整可能です。
設定オプションについては以下の通りです。
オプション | 型 | 説明 |
---|---|---|
wrapper |
| スクロールコンテナとして使用される要素 |
content |
| スクロールされるコンテンツを含む要素 |
eventsTarget |
| |
smoothWheel |
|
|
lerp |
| 線形補間強度(0~1) |
duration |
| スクロールアニメーションの継続時間(秒) |
easing |
| スクロールアニメーションに使用するイージング関数 |
orientation |
| スクロールの方向 |
gestureOrientation |
| スクロールやスワイプなどのジェスチャー入力の方向 |
syncTouch |
| タッチデバイスのスクロールを制御するかどうか |
syncTouchLerp |
|
|
touchInertiaMultiplier |
|
|
wheelMultiplier |
| マウスホイールによるスクロール速度の設定に使用する乗数 |
touchMultiplier |
| タッチイベントによるスクロール速度の設定に使用する乗数 |
infinite |
| 無限スクロールを有効にするか |
autoResize |
| リサイズ時にインスタンスのサイズを自動的に変更するかどうか |
prevent |
| スムーススクロールを特定の要素に対して無効にする |
virtualScroll |
| スクロールイベントが処理される前に、任意の処理を実行 |
overscroll |
| CSSの |
autoRaf |
|
|
anchors |
| アンカーリンクを有効にするかどうか。 |
例えばスクロールの重たさ(速さ)を調整する場合、lerp
やduration
を変更することで微調整が可能です。
(※ただし、duration
とeasing
はlerp
とは共存しません)
const lenis = new Lenis({
lerp: 0.6,
})
/* または */
const lenis = new Lenis({
duration: 1,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
})
値を適宜調整しながら、プロジェクトに適したアニメーションを模索してみてください。
注意事項
Lenisは通常のスクロール動作をJavaScriptで上書きするため、スクロールが関係するUI全般に影響を与えます。
場合によってはユーザビリティが低下したり、特定のUIが操作不能になったりするなどの弊害が生まれることもあります。
ここでは代表的な落とし穴と解決方法について説明します。
アンカーリンク(スムーススクロール)の実装
アンカーリンク(<a href="#*">
)を実装する際、一般的にはクリック時に対象のセクションまでスムーズにスクロールされる動作(スムーススクロール)が求められます。
実装方法としては主に以下の例が挙げられます。
/* CSSでの実装例 */
html {
scroll-behavior: smooth;
}
/* JavaScriptでの実装例 */
const anchorlink = document.querySelectorAll('a[href^="#"]')
anchorlink.forEach((link) => {
link.addEventListener('click', (e) => {
e.preventDefault()
const target = document.querySelector(link.hash)
if (target) {
target.scrollIntoView({
block: 'start',
behavior: 'smooth',
})
}
})
})
behavior: smooth
はブラウザエンジンがスクロールアニメーションの計算を行うため、よりシンプルなコードで実装できます。
ただし、Lenisを導入している場合はJavaScriptがスクロールアニメーションの計算を行うため、上記の方法で実装するとアニメーションの衝突が発生し、スムーススクロール時にガタつきやジャンプなどが発生してしまいます。
よって、Lenisを導入するプロジェクトではbehavior: smooth
でのスムーススクロールの実装は推奨されていません。
スムーススクロールを実装する際はLenisの機能を使用します。
実装方法は主に以下の2パターンがあります。
例1. anchorsオプションを使用する場合
const lenis = new Lenis({
anchors: true
})
/* ScrollToオプションを使用する場合 */
const lenis = new Lenis({
anchors: {
offset: -80,
duration: 1,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
lock: true
}
})
インスタンス生成時にanchors
オプションを設定することで、アンカーリンクのスクロールアニメーションが適用されるようになります。
anchors
オプションはブール値またはScrollToOptions
を渡します。ScrollToOptions
の内訳は以下の通りです。
プロパティ | 型 | 説明 |
---|---|---|
offset |
| scroll-padding-topに該当する値 |
lerp |
| 線形補間強度(0~1) |
duration |
| スクロールアニメーションの継続時間(秒) |
easing |
| スクロールアニメーションに使用するイージング関数 |
immediate |
| アニメーション( |
lock |
| ターゲットに到達するまでスクロールできないようにするかどうか |
force |
| インスタンスが停止状態でも強制的に実行させるかどうか |
onComplete |
| ターゲットに到達した時に呼び出すコールバック |
userData |
|
|
例2. scrollToメソッドを使用する場合
const lenis = new Lenis()
const anchorlink = document.querySelectorAll('a[href^="#"]')
anchorlink.forEach((link) => {
link.addEventListener('click', (e) => {
e.preventDefault()
const target = document.querySelector(link.hash)
if (target) {
lenis.scrollTo(target, {
offset: -80,
duration: 1,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
lock: true
})
}
})
})
anchors
オプションを使用せずにLenisのscrollTo
メソッドを直接呼び出す方法もあります。scrollTo
メソッドを呼び出す際はスクロール先の要素(target
)とオプション(ScrollToOptions
)を渡します。ScrollToOptions
の設定項目はanchors
オプションと同様ですが、こちらの手法はtarget
要素を変更できるなど拡張性が高い点が特徴です。
スクロールのネスト
実装を進める上で、モーダルウィンドウやドロワーメニューなど、メインコンテンツとは別に、特定の要素内でのみスクロールさせたいケースがあります。
ただし、Lenisを導入している場合、スクロールコンテナ(wrapper
オプション)内の要素全てのスクロールがJavaScriptで上書きされるため、特定の要素内でスクロールさせようとしても、メインコンテンツにスクロールが伝播してしまい、要素内でスクロール動作を独立させることができません。
特定の要素内でスクロール動作を独立させるには以下の2つの方法があります。
例1. preventオプションを使用する方法
<div id="modal">スクロール可能コンテンツ</div>
const lenis = new Lenis({
prevent: (node) => node.id === 'modal'
})
インスタンス生成時にprevent
オプションを設定することで、特定の要素に対してスクロール動作を独立させることができます。prevent
オプションでは、引数にスクロールコンテナ内に存在する要素(node
)を受け取り、関数がtrue
を返すと、その要素に対してスクロールアニメーションが無効化されます。
例2. Lenisのdata属性を使用する方法
Lenisではスクロールのネスト処理を簡易的に実装できるよう、専用のdata属性が準備されています。
例えば、例1で紹介した処理は以下の方法でも実装できます。
<div data-lenis-prevent>スクロール可能コンテンツ</div>
ホイールイベントやタッチイベントといった特定のイベントのみ無効化したい場合は以下のように記述します。
<!-- ホイールイベントのみ無効化 -->
<div data-lenis-prevent-wheel>スクロール可能コンテンツ</div>
<!-- タッチイベントのみ無効化 -->
<div data-lenis-prevent-touch>スクロール可能コンテンツ</div>
応用(スクロール方向の検知)
Lenisでは、慣性スクロール以外のスクロール関連の処理についても簡易的に実装できます。
今回はデモページでも実装している「下方向にスクロールした際にヘッダーを非表示にし、上方向にスクロールした際にヘッダーを表示」する処理を追加してみます。
const lenis = new Lenis()
const header = document.getElementById('header')
/* スクロールイベントの呼び出し */
lenis.on('scroll', ({ direction }) => {
header.classList.toggle('is-hidden', direction === 1)
})
Lenisのon
メソッドを使用してscroll
イベントを呼び出し、イベントの引数にスクロール方向(direction
)を受け取ります。direction
は1
が下方向のスクロール、-1
が上方向のスクロールを示す値となるため、この値を元に.is-hidden
クラスを追加/削除しています。
scroll
イベントではdirection
以外にもスクロールに関する情報を引数で受け取れます。
詳細は公式の「Properties」をご参照ください。
参考文献
・Lenis – Get smooth or die trying