プログラミング

【PR】を含みます。

【JavaScript】ライブラリ未使用で慣性スクロールを実装する方法

JavaScript ライブラリ未使用で慣性スクロールを実装する方法

この記事では、JavaScriptを使ってライブラリなしで慣性スクロールを実装する方法を紹介します。

サンプルとして、このページを慣性スクロールに対応させてます。

【実装コード】requestAnimationFrame()メソッドで慣性スクロールを実装

HTML
Copy
  1. <div class="inertia-scroll">
  2.   <p>ここにコンテンツが入ります。</p>
  3. </div>
JavaSctipt
Copy
  1. (function() {
  2.   // 慣性スクロールを適用したい要素を指定
  3.   const scrollElements = document.querySelector('.inertia-scroll');
  4.   // 要素が存在しない場合は処理を中止
  5.   if (!scrollElements) {
  6.     return;
  7.   }
  8.   let currentScroll = window.scrollY;  // 現在のスクロール位置
  9.   let targetScroll = 0;                // 目標スクロール位置
  10.   const ease = 0.1;                    // 慣性(0に近いほどスムーズになる)
  11.   const threshold = 0.1;               // 差がこの値以下ならリセット
  12.   // 要素にスタイルを適用
  13.   scrollElements.style.cssText =
  14.     'position: fixed;' +
  15.     'width: 100%;' +
  16.     'top: 0;' +
  17.     'left: 0;';
  18.   // bodyに高さを設定
  19.   document.body.style.height = scrollElements.clientHeight + 'px';
  20.   // ページ更新対策
  21.   window.addEventListener('beforeunload', function() {
  22.     // ページ更新前にwindow.scrollYの値をsessionStorageに格納
  23.     sessionStorage.setItem('scrollY', window.scrollY);
  24.   });
  25.   let isReloaded = false;
  26.   const entries = performance.getEntriesByType('navigation');
  27.   if (entries[0] && entries[0].type === 'reload') {
  28.     isReloaded = true;
  29.   }
  30.   // ページ更新が更新された場合、sessionStorageからwindow.scrollYの値を取得
  31.   if (isReloaded && sessionStorage.getItem('scrollY')) {
  32.     currentScroll = Number(sessionStorage.getItem('scrollY'));
  33.     targetScroll = currentScroll;
  34.     window.scrollTo({
  35.       top: currentScroll,
  36.       left: 0,
  37.       behavior: 'auto'
  38.     });
  39.   }
  40.   // アニメーションを開始
  41.   updateScroll();
  42.   // スクロール位置を更新する関数
  43.   function updateScroll() {
  44.     if (Math.abs(targetScroll - currentScroll) < threshold) {
  45.       // 差が閾値以下ならリセット
  46.       currentScroll = targetScroll;
  47.     } else {
  48.       // 現在のスクロール位置を目標位置に近づける
  49.       currentScroll += (targetScroll - currentScroll) * ease;
  50.     }
  51.     // 位置を更新
  52.     scrollElements.style.transform = 'translate3d(0, -' + currentScroll + 'px, 0)';
  53.     // アニメーションフレームを設定
  54.     requestAnimationFrame(updateScroll);
  55.   }
  56.   // スクロールイベントで目標スクロール位置を更新
  57.   window.addEventListener('scroll', function() {
  58.     targetScroll = window.scrollY;
  59.   });
  60. })();

注意事項

  1. 固定ヘッダー等の追従要素(position: fixedを指定している要素)は、<div class="inertia-scroll"></div>の外に記述する必要があります。

    慣性スクロール実現するため、<div class="inertia-scroll"></div>transformプロパティを設定してます。

    ※CSSの仕様上、transformプロパティが指定された要素にposition: fixedを持つ要素を指定した場合、ビューポート(画面全体)を基準とするのではなく、transformを持つ親要素を基準に固定されるようになります。

  2. <div class="inertia-scroll"></div>にページ内リンクがある場合、機能しない場合があります。

    ページ内リンクが機能しないときの対策方法は以下にまとめてます。

  3. <div class="inertia-scroll"></div>にアコーディオンメニュー等で動的に高さが変化する要素がある場合、bodyの高さを調整する必要あります。

    また、動的に要素を追加したり、非同期で要素を追加する場合もbodyの高さを調整する必要あります。

    動的に高さが変わるコンテンツがあるときの対応方法は以下にまとめてます。

  4. ほどんどのモダンブラウザに対応してますが、古いブラウザで動作しない場合があります。

<div class="inertia-scroll"></div>position: fixedを指定しているため、通常の方法ではページ内リンクが機能しない状態になっています。

別途JavaScriptで要素の位置を取得して、スクロールさせるいう処理が必要になります。

実装コード

JavaSctipt
Copy
  1. (function() {
  2.   // 慣性スクロールを適用したい要素を指定
  3.   const scrollElements = document.querySelector('.inertia-scroll');
  4.   // 要素が存在しない場合は処理を中止
  5.   if (!scrollElements) {
  6.     return;
  7.   }
  8.   let currentScroll = window.scrollY;  // 現在のスクロール位置
  9.   let targetScroll = 0;                // 目標スクロール位置
  10.   const ease = 0.1;                    // 慣性(0に近いほどスムーズになる)
  11.   const threshold = 0.1;               // 差がこの値以下ならリセット
  12.   // 要素にスタイルを適用
  13.   scrollElements.style.cssText =
  14.     'position: fixed;' +
  15.     'width: 100%;' +
  16.     'top: 0;' +
  17.     'left: 0;';
  18.   // bodyに高さを設定
  19.   document.body.style.height = scrollElements.clientHeight + 'px';
  20.   // ページ更新対策
  21.   window.addEventListener('beforeunload', function() {
  22.     // ページ更新前にwindow.scrollYの値をsessionStorageに格納
  23.     sessionStorage.setItem('scrollY', window.scrollY);
  24.   });
  25.   let isReloaded = false;
  26.   const entries = performance.getEntriesByType('navigation');
  27.   if (entries[0] && entries[0].type === 'reload') {
  28.     isReloaded = true;
  29.   }
  30.   // ページ更新が更新された場合、sessionStorageからwindow.scrollYの値を取得
  31.   if (isReloaded && sessionStorage.getItem('scrollY')) {
  32.     currentScroll = Number(sessionStorage.getItem('scrollY'));
  33.     targetScroll = currentScroll;
  34.     window.scrollTo({
  35.       top: currentScroll,
  36.       left: 0,
  37.       behavior: 'auto'
  38.     });
  39.   }
  40.   // アニメーションを開始
  41.   updateScroll();
  42.   // スクロール位置を更新する関数
  43.   function updateScroll() {
  44.     if (Math.abs(targetScroll - currentScroll) < threshold) {
  45.       // 差が閾値以下ならリセット
  46.       currentScroll = targetScroll;
  47.     } else {
  48.       // 現在のスクロール位置を目標位置に近づける
  49.       currentScroll += (targetScroll - currentScroll) * ease;
  50.     }
  51.     // 位置を更新
  52.     scrollElements.style.transform = 'translate3d(0, -' + currentScroll + 'px, 0)';
  53.     // アニメーションフレームを設定
  54.     requestAnimationFrame(updateScroll);
  55.   }
  56.   // スクロールイベントで目標スクロール位置を更新
  57.   window.addEventListener('scroll', function() {
  58.     targetScroll = window.scrollY;
  59.   });
  60.   // すべてのリンクにイベントを追加
  61.   document.querySelectorAll('a[href^="#"]').forEach(function (link) {
  62.     link.addEventListener('click', function (event) {
  63.       event.preventDefault(); // デフォルトの動作を無効化
  64.       const adjust = 0; // スクロール位置の調整
  65.       const speed = 400; // スクロール速度(ms)
  66.       const href = this.getAttribute('href'); // href属性を取得
  67.       const target = href === '#' || href === '' ? document.documentElement : document.querySelector(href);
  68.       if (target) {
  69.         const position = target.offsetTop - adjust;
  70.         // スムーズスクロール対応か確認
  71.         if ('scrollBehavior' in document.documentElement.style) {
  72.           // スムーズスクロールを使用
  73.           window.scrollTo({ top: position, behavior: 'smooth' });
  74.         } else {
  75.           // フォールバック:アニメーションなしで移動
  76.           window.scrollTo(0, position);
  77.         }
  78.       }
  79.     });
  80.   });
  81. })();

動的に高さが変わるコンテンツがあるときの対応方法

<div class="inertia-scroll"></div>に高さが動的に変化する要素がある場合、高さが変化するたびにbodyの高さを更新する必要があります。

高さの更新方法は様々な方法がありますが、今回はResizeObserver<div class="inertia-scroll"></div>の高さの変化を監視して、変化がればbodyの高さを更新する方法を採用しました。

実装コード

JavaSctipt
Copy
  1. (function() {
  2.   // 慣性スクロールを適用したい要素を指定
  3.   const scrollElements = document.querySelector('.inertia-scroll');
  4.   // 要素が存在しない場合は処理を中止
  5.   if (!scrollElements) {
  6.     return;
  7.   }
  8.   let currentScroll = window.scrollY;  // 現在のスクロール位置
  9.   let targetScroll = 0;                // 目標スクロール位置
  10.   const ease = 0.1;                    // 慣性(0に近いほどスムーズになる)
  11.   const threshold = 0.1;               // 差がこの値以下ならリセット
  12.   // 要素にスタイルを適用
  13.   scrollElements.style.cssText =
  14.     'position: fixed;' +
  15.     'width: 100%;' +
  16.     'top: 0;' +
  17.     'left: 0;';
  18.   // bodyに高さを設定
  19.   document.body.style.height = scrollElements.clientHeight + 'px';
  20.   // ページ更新対策
  21.   window.addEventListener('beforeunload', function() {
  22.     // ページ更新前にwindow.scrollYの値をsessionStorageに格納
  23.     sessionStorage.setItem('scrollY', window.scrollY);
  24.   });
  25.   let isReloaded = false;
  26.   const entries = performance.getEntriesByType('navigation');
  27.   if (entries[0] && entries[0].type === 'reload') {
  28.     isReloaded = true;
  29.   }
  30.   // ページ更新が更新された場合、sessionStorageからwindow.scrollYの値を取得
  31.   if (isReloaded && sessionStorage.getItem('scrollY')) {
  32.     currentScroll = Number(sessionStorage.getItem('scrollY'));
  33.     targetScroll = currentScroll;
  34.     window.scrollTo({
  35.       top: currentScroll,
  36.       left: 0,
  37.       behavior: 'auto'
  38.     });
  39.   }
  40.   // アニメーションを開始
  41.   updateScroll();
  42.   // スクロール位置を更新する関数
  43.   function updateScroll() {
  44.     if (Math.abs(targetScroll - currentScroll) < threshold) {
  45.       // 差が閾値以下ならリセット
  46.       currentScroll = targetScroll;
  47.     } else {
  48.       // 現在のスクロール位置を目標位置に近づける
  49.       currentScroll += (targetScroll - currentScroll) * ease;
  50.     }
  51.     // 位置を更新
  52.     scrollElements.style.transform = 'translate3d(0, -' + currentScroll + 'px, 0)';
  53.     // アニメーションフレームを設定
  54.     requestAnimationFrame(updateScroll);
  55.   }
  56.   // スクロールイベントで目標スクロール位置を更新
  57.   window.addEventListener('scroll', function() {
  58.     targetScroll = window.scrollY;
  59.   });
  60.   // 監視対象の要素を取得
  61.   const targetElement = document.querySelector('.inertia-scroll');
  62.   // ResizeObserverのインスタンスを作成
  63.   const resizeObserver = new ResizeObserver((entries) => {
  64.     entries.forEach((entry) => {
  65.       document.body.style.height = entry.contentRect.height + 'px';
  66.     });
  67.   });
  68.   // 要素の監視を開始
  69.   resizeObserver.observe(targetElement);
  70. })();

udemyでJavaScriptを学ぶ

学習時間
24.5時間
学習内容
  • 本格的なWEBサイトを作成する方法について学びます。
  • Sass(※以下CSSと記載)、JavaScriptの基礎について深く学びます。
  • CSS、JavaScriptの実践的な記述について深く学びます。
  • CSS、JavaScriptアニメーションの実装について学びます。
  • 最新の実践的なWEB画面の作成方法について深く学びます。
  • CSS、JavaScriptのコードの最適化、安定化について学びます。
  • 維持管理、持続可能なコードの記述方法について学びます。
対象受講者
  • ドットインストールでCSS、JavaScriptの入門編を終えたレベルの方
  • 他の先生のフロントエンドの入門編を終えたレベルの方
  • 自分で本格的なWEBサイトを作ってみたい方
  • CSS、JavaScriptの実践的な基礎を効率的に学びたい方
  • CSS、JavaScriptを今後仕事で使う予定の方
  • CSS、JavaScriptのコードの整理方法について学びたい方
  • CSS、JavaScriptのアニメーションを学びたい方
  • CSS、JavaScriptのレベルアップしたいWeb開発初級者の方
  • CSS、JavaScriptでどのようにすればレベルアップできるのか悩んでいる方
アイコン画像

もみじ

HTML・CSS・JavaScripの基礎的なことを理解しており、Webのフロントエンジニアを目指している方にオススメの講座です。

講座内容は実践で使用するものが多く、実務でも役立つ内容でした。

学習時間
20.5時間
学習内容
  • JavaScriptの動作原理について深く学びます。
  • JavaScriptがどのように実行されるのかについて深く学びます。
  • ES6+の最新のJavaScriptの記法について幅広く学びます。
  • 変数や参照の仕組みについて深く学びます。
  • オブジェクトのメカニズムについて深く学びます。
  • 関数のメカニズムについて深く学びます。
  • スコープの仕組みについて深く学びます。
  • プロトタイプのメカニズムについて深く学びます。
  • 反復処理のメカニズムについて深く学びます。
  • ジェネレーターやイテレーターについて深く学びます。
  • コールバック関数について深く学びます。
  • 非同期処理のメカニズムについて深く学びます。
  • モジュールの仕組みについて深く学びます。
  • クロージャーの仕組みについて深く学びます。
  • レキシカルスコープの仕組みについて深く学びます。
  • アロー関数の特徴について深く学びます。
  • レキシカルスコープの仕組みについて深く学びます。
  • アロー関数の特徴について深く学びます。
  • thisの仕組みについて深く学びます。
  • bind、apply、callの動作原理について深く学びます。
  • クラスと継承の仕組みについて深く学びます。
  • ReflectやProxyなどの強力なオブジェクトへの理解を深めます。
  • Map,Setなどの強力なコレクションについて実践を交えて学びます。
  • よく使用される強力な実装パターンを演習を交えて学びます。
  • 独自のフレームワークを構築していく中でJavaScriptのメカニズムについて理解を深めます。
対象受講者
  • JavaScriptをコアの動作原理からキチンと理解したい方。
  • 自分の思った通りコードが動かずに悩んでいる方。
  • 自分の思った通りに動かず、気付いたら解決するのに1日経ってしまっていた経験のある方。
  • React, Vue, Angularが使いこなせず悩んでいる方。
  • JavaScriptの不可解な動きが理解できず、苦しんでいる方。
  • 少し複雑なコードを書こうとすると、ぐちゃぐちゃになって整理しきれず、悩んでいる方。
  • JavaScriptを本気で勉強したいと思っている方。
  • JavaScriptの変態仕様に苦しんでいる方。
  • JavaScriptが苦手なプログラマーの方。
アイコン画像

もみじ

JavaScripの基礎的なことを理解しており、より深くJavaScriptについて理解したいという方にオススメの講座です。

初心者の方が一度の受講で全てを吸収するのは難しいですが、繰り返し受講することでフロントエンドの業務で役立つ知識を身に付けることができます。

HTMLとCSSの知識がありJavaScriptを学びたいという方におすすめの1冊

アイコン画像

もみじ

HTMLとCSSの知識がありJavaScriptを学びたいという方に入門書としておすすめの書籍です。

実務で役立つサンプルを手を動かしながら学ぶことができ、実践的なスキルを身に付けることができます。

JavaScriptのフロントエンドエンジニアを目指している方におすすめの1冊

アイコン画像

もみじ

この本はJavaScript初心者からフロントエンドエンジニアを目指す方にぴったりの1冊です。

非同期処理やAJAXなど、現代の開発で必要なスキルがわかりやすく解説されており、実務で役立つスキルを学ぶことができます。

-プログラミング
-,