• TOP
  • ARTICLE
  • CATEGORY

    • #組織
    • #PR
    • #UX
    • #サイト制作
    • #サイト改善
    • #Gatsby.js
    • #JavaScript
    • #CSS
    • #HTML
  • ABOUT

lazyloadとsmoothscrollを併用するシンプルな方法

さまざまなページでユーザー体験を改善するために使えるテクニックとしてレイジーロード(lazyload)スムーススクロール(smoothscroll)があります。
どちらも便利な手法ですが、組み合わせて使うと困った事態になることも。
この記事ではその解決策を紹介していきます。

lazyloadとsmoothscrollとは

そもそもlazyloadとsmoothscrollってなに?」という方は以下の説明をご一読ください。
組み合わせバグの解決策を知りたいんだ!」という方はこの章を飛ばしてお進みください。

lazyload

lazyloadは画像をあと読み込みにすることで、ページロードを早める処理です。
画像が多くなるとページ容量が増えて、ロード時間がどうしても長くなっていきます。
ロード時間はユーザー体験に直結しており、「ページロードに3秒かかると半分のユーザーは離脱する」というGoogleの調査もあります。
とはいえ、「ページロード時間も大切だけど、高画質の画像を使ってリッチな印象をユーザーに与えたい!」ということもありますよね。
そんな時に便利なのがlazyloadです。
怠惰なロードの名の通り、実際に画像が必要になるまで/必要になりそうなまで、ロードを実施しないのがlazyloadです。
lazyload実装をすると、ページロード時にはファーストビュー近辺で必要なアセットだけを読み込むようになり、最初にロードするものを大幅に減らすことができます。
その後スクロールに応じて必要なものを直前で都度読み込んでいく、非常にスマートな手法です。
少し前まではlazyload実現のためには複雑な実装をしたり、プラグインを読み込む必要があったりしましたが、最近ではブラウザーに標準搭載になってきているため驚くほど簡単に実現可能になっています。
ブラウザー標準機能を使うには画像に loading="lazy" を追加で指定するだけです。
2020年末時点のステータス:Safariが実装中、IEは非対応、他の主要ブラウザーは対応済み

smoothscroll

その名の通りスムーズにスクロールさせるための処理です(「なめらかスクロール」などの呼び方もあります)。
ページ内リンクを実装する際、普通に実装すると一瞬で目的地に到着してしまいます。
ユーザーはスクロールが発生したかどうかもよく分からず、ページ内のどのあたりの位置に来てしまったのか、はたまた別のページに来てしまったのかなどで混乱します。
また、ページ途中のコンテンツに気づいてもらえなくなる懸念もあるでしょう。
この課題を解決する手法がsmoothscrollです。
ページ内リンクが押された際に、アニメーションで目的の位置までスクロールをさせます。
やっていることはシンプルですが、ユーザーにとっては何が起きたのかが分かりやすくなり、簡単にUX向上を図ることができるテクニックです。

組み合わせた時に発生する問題

以上ご説明してきたように、lazyloadとsmoothscrollはユーザー体験を大きく向上させる素晴らしいテクニックです。
実際の案件でこの二つをぜひ使ってみてください!」..と言って終わりたいところなのですが、組み合わせて使うとちょっと困った事象が発生します。

スクロールの目的地がズレる

lazyloadを導入した場合、ページロードのタイミングでは、ページの下の方にある要素(ファーストビューから遠い要素)は読み込まれておらず、高さも0になっています。
その状態でsmoothscrollをするとどうなるのでしょうか?
以下の画像でセクション①からセクション③へsmoothscrollをするケースで考えてみましょう。
スクロールの開始時点では、セクション②に存在する画像が読み込まれておらず、画像の高さは0として目的地のY座標を計算します。
しかし、いざスクロールをしていくと、セクション②にある画像が読み込まれ、実際のサイズに応じて画像の高さが領域として取られます
その結果として、セクション③のY座標がズレるため、当初の目的地であるY座標までスクロールしたとしても、実際にセクション③が始まる位置とは乖離してしまっているのです。
少し説明が複雑ですが、一言で言うと「ゴールの座標が途中で変わるため、最初に目指していた地点までいってもダメ」という感じでしょうか。

この問題を解決するには

ではどうすればこの問題を解決できるのでしょうか?
一番簡単な方法は画像1枚1枚にheightを指定しておくことでしょう。
サイズを指定しておけば、画像が読み込まれる前からその領域が確保されるので、実際の読み込みが進んでもゴールの座標は変わりません。
Googleが近年ウェブのユーザー体験の重要指標として提唱するWebVitalsのCLS(Cumulative Layout Shift。読み込みが進んでいく際に画面のレイアウト変更がどれだけ起きたか)の向上施策としても、画像のサイズを指定しおくのは良い手法だと考えらます。
というわけで、これが美しい解決策ではあるのですが、実務では「どんなサイズの画像が入ってくるか分からない」、「画像1枚ずつにサイズを指定するのが面倒」などの課題があるかと思います。
そんな時に使えるJSの実装をご紹介していきます(やっと本題)

再起処理を入れたsmoothscroll

まずはコード全体です↓
smoothscroll

const smoothScroll = () => { // スクロールの速度 const SPEED_DEFAULT = 800; // 最初の速度 const SPEED_REPEAT = 100; // 繰り返し時の速度 /** スクロール先の位置を取得 @module getTargetPositionY @param {object} $this - スクロール先のjQueryオブジェクト @return {number} - スクロール先のY位置 */ const getTargetPositionY = ($this) => { // 移動先を取得 const href = $this.attr('href'); const target = $(href == '#' || href == '' ? 'html' : href); // 移動先のY座標を返却 return target.offset().top; } /** アニメーション処理 @module scrollAnimate @param {object} $this - スクロール先のjQueryオブジェクト @param {number} positionStart - スクロール開始時点のスクロール先 @param {number} scrollSpeed - スクロール速度 */ const scrollAnimate = ($this, positionStart, scrollSpeed) => { $('html').animate({ scrollTop: positionStart }, scrollSpeed, 'swing', () => { // スクロールアニメーション終了時に、目的地のY座標を取得 const positionEnd = getTargetPositionY($this); // 処理を繰り返すかの判定 // 目的地のY座標が変わっていたらやり直し if(positionStart !== positionEnd) { scrollAnimate($this, positionEnd, SPEED_REPEAT); } }); } $('a[href^="#"]').on('click.smoothScroll', function () { const positionStart = getTargetPositionY($(this)); scrollAnimate($(this), positionStart, SPEED_DEFAULT); }); };
少し長いですが、注意して見てもらいたいのは以下の部分だけです。
scrollAnimate

const scrollAnimate = ($this, positionStart, scrollSpeed) => { $('html').animate({ scrollTop: positionStart }, scrollSpeed, 'swing', () => { // スクロールアニメーション終了時に、目的地のY座標を取得 const positionEnd = getTargetPositionY($this); // 処理を繰り返すかの判定 // 目的地のY座標が変わっていたらやり直し if(positionStart !== positionEnd) { scrollAnimate($this, positionEnd, SPEED_REPEAT); } }); }
この記述ではアニメーションのスクロール処理を記述しています。
アニメーション処理終了時のコールバック部分がメインになっており、そこでスクロールした先が本当に正しい目的地なのか(最初に目指していた目的地と変更がないか)をチェックしています。
そのチェックに通過すれば正しい目的地に到達できたということで処理を終了し、もし通過しなければ正しい目的地ではないので同じ処理を再度実行します。
この処理を正しいゴールに到達するまで繰り返す、というのが実装の概要です。
ちょっと力技ですが、自分の手元ではうまく動いています。
(ページの長さなどに応じて、スクロールスピードの調整が必要なケースもありそうです)

まとめ

以上、lazyloadとsmoothscrollを組み合わせた際に起こる問題とその解決策についてご説明しました。
どちらもUX向上に便利なテクニックなので、どちらか一方を諦めることなく、この記事で紹介したような解決策を使って併用を目指してみてください。

この記事をシェアしてくれると小躍りします

Recent Article