jQuery を使って、ページスクロールに合わせてスクロール量を可視化するカラーバーを配置する

APOC というサイトを見ていたら、画面のスクロールに合わせて、ページ上部に赤い棒線が引かれていくエフェクトを発見した。ページの最上部にいると棒線は見えておらず、下にスクロールしていくと棒線が伸びていき、ページ最下部に着くと棒線が画面右端にまで広がるという効果だった。

面白かったのでソースを解析し、少し手直ししてまとめた。以下で動作確認できる。

まずは空の div 要素 #scroll-colour-bar を作って配置する。

<!-- スクロール量に合わせて幅が可変するカラーバー -->
<div id="scroll-colour-bar"></div>

次に以下のような CSS を定義しておいて画面左上に固定配置しておく。ここから jQuery で width を動的に変化させる寸法だ。

#scroll-colour-bar {
  /* Fixed でスクロールしても画面上部に固定配置するようにする */
  position:fixed;
  top:0;
  left:0;
  /* デフォルトは幅 0 にしておくことでチラつきをなくす */
  width:0;
  /* 棒線の太さと色の指定 */
  height:5px;
  background:#08f;
}

動的に width を変化させるスクリプトは以下のとおり。

const scrollColourBar = () => {
  // requestAnimationFrame を使って描画処理を行わせる
  window.requestAnimationFrame(() => {
    const scrollAmount   = $(document).scrollTop();
    const documentHeight = $(document).height();
    const windowHeight   = $(window).height();
    // 画面の高さとスクロール量から「スクロール率」を計算する
    const scrollWidth = scrollAmount / (documentHeight - windowHeight) * 100;
    // 計算した「スクロール率 (パーセンテージ)」を width として適用する
    const targetElem = $('#scroll-colour-bar');
    targetElem.css('width', scrollWidth + '%');
  });
};

Babel でトランスパイルする前提で、ES6 (ES2015) チックな書き方をしているが、アロー関数 () => {}function() {} に、定数宣言の constvar に書き換えればレガシー JavaScript として動作するのでお好みに合わせて直してもらいたい。

window.requestAnimationFrame というものを初めて知ったのだが、何やらアニメーション処理させる時に画面がアクティブかどうかとかを見てエコに処理してくれるモノっぽいので使ってみた。

この関数を onscroll イベントで呼ぶようにしておけば、スクロールに追随して width が動的に変化するという仕組み。また今回は、初期表示する DOMContentLoaded (jQuery の Ready) のタイミングと、ウィンドウをリサイズすることでテキストの折り返しとかでページの高さが変わることを想定して、onresize のタイミングでも呼ぶようにした。初期表示のタイミングは DOMContentLoaded だと画像読み込みで高さが変わったりするかも?様子を見て onload で呼ぶようにしても良いかもしれない。

ということでスクリプト全体としては以下のようになる。

$(() => {
  // スクロール量に合わせて要素の幅を変える関数
  const scrollColourBar = () => {
    window.requestAnimationFrame(() => {
      const scrollAmount   = $(document).scrollTop();
      const documentHeight = $(document).height();
      const windowHeight   = $(window).height();
      const scrollWidth = scrollAmount / (documentHeight - windowHeight) * 100;
      const targetElem = $('#scroll-colour-bar');
      targetElem.css('width', scrollWidth + '%');
    });
  };
  
  // 初期表示時 (ハッシュ付きでアクセスした場合にもスクロールする)
  scrollColourBar();
  
  // スクロール・リサイズ時に再描画する
  $(window).on('scroll resize', scrollColourBar);
});

簡単だけど面白い効果でした。