JavaScript追加のみでスムーススクロール実装する [Safari, iOS対応]

ページ内リンクで遷移する際に, 目的の箇所にパッとジャンプするのではなく, スムースにスクロールして移動させたかった.

これは, その遷移が画面内の上下どっちに行くのかを直感的にわかるようにしたかったため.

簡単実装する際には, cssで全体に対して

といったことをすれば実現できる. ただし, この手法だと, Safari, iOS上のChrome, Safariなどでは機能せず, パッとジャンプになってしまう.

そこで, JavaScriptをHeadに読み込むだけで, aタグのページ内リンク (href="#hoge"等) スムーススクロールになるようなものを作成してみた.

  • aタグに特別な変更が要らずHeadでの読み込みだけなので導入が楽なこと
  • jQueryを利用する手法よりも軽いこと

などがメリットに挙げられると思う. また, スクロールの滑らかさについてもある程度調整可能なようにした.

 

動画

 

実際の挙動

テストページにて実際の動作を確認できる. 以下は実際の動作のアニメ.

 

Macbook Air (M1 2020)

SafariChrome
sample_safari_10ms_10div_-50shift.movsample_chrome_10ms_10div_-50shift.mov

iPhone 8

SafariChrome
iPhone_safari.mp4iPhone_chrome.mp4

 

実装

上記のテストページのサンプルコードを掲載する. 以下の3ファイルを同一ディレクトリに入れ, index.htmlを開けば動作する. インストール等は不要.

 

index.html

 

<script defer type="text/javascript" src="smooth-scroll.js"></script>

のように, jsの読み込み指定をdeferにしている. asyncで実装していた時期もあったが, その際, たまにjsがうまく動作しないことがあった (jsが先に実行されてしまっていた?).

参考: スクリプトの非同期読み込み(async, deferの違い)

 

stylesheet.css

scroll-behavior: smooth はあってもなくても良い.

実際のスクロール時にのみ無効化するようにしているため

 

.indexWrapperの部分も目次を右側に固定表示するためのものなので, 今回の実装の本質ではない.

 

smooth-scroll.js

実装内容としては, 現在位置から目的の箇所まで, 細かいジャンプを繰り返して到達している. その細かさがある程度細かいことにより, スムースにスクロールしているように見える.

 

以下が調整パラメータ:

  • const interval_ms = 10;

    • 細かいジャンプをする時間間隔 [ミリ秒]
  • const move_division = 10.0;

    • 細かいジャンプの移動割合

    • 現在位置から目的の箇所までの距離をYとした場合

      • 10なら, 110Y, 9100Y, 811000Y,  間隔ずつ進んでいく.
      • 2なら, 間隔ずつ進んでいく.
  • const y_shift = -50;

    • スクロールした際の上下のシフト量 [px]
    • 負の場合, 本来の目的の箇所より下に到達する
    • 正の場合, 本来の目的の箇所より上に到達する
  • const y_threhold = 1;

    • 目的の箇所付近でスクロールを終える終了条件として閾値 [px]
    • スクロールしていき, 目的の箇所との誤差がこの閾値px以下になったら目的箇所にジャンプし, スクロールを終了する.

 

基本的には, そのままで良いと思うが, 必要に応じて, interval_ms, move_division, y_shift あたりを調整すると良い. y_thresholdは調整する必要はほとんどないと思われる.

 

 

参照

その他