スクロールに応じて変化する読了率メーターを実装してみる

2022/11/12
スクロールに応じて変化する読了率メーターを実装してみる

サイトを見ているとたまに見かける読了率メーター。記事をどのあたりまで読んだかが視覚的にわかりやすくて良いですよね。

そんな読了率メーターが簡単に導入できたら良いなぁ…ということでコピペで簡単に使えるものを作ってみました。最低限のHTML, CSS, JavaScriptで構成されたシンプルな読了率メーターです。

バー型

ベーシックなバー型の読了率メーターです。読んだ領域(表示された領域)の変化に応じてバーの状態が変化します。

対象部分がまだ読まれていない場合はデフォルトのグレーで、読んだ割合に応じて赤色部分が伸びていき、全部読んだ場合は赤100%になる仕様です。言葉だけだとイメージしにくいので、以下のCodePenのデモで実際の動きを確かめてみてください。

See the Pen Progress Bar by Fuma (@fuma_it) on CodePen.

画面をスクロールすると、記事本体部分にあたる.post-bodyにさしかかったときバーが動き始め、記事本体の最下部まで表示されたときにバーが100%になります。

ソースコード

ソースコードは以下の通りです。

HTML

<div class="progress-bar"></div>

CSS

.progress-bar {
  position: fixed;
  top: 10px;
  left: 10px;
  width: calc(100% - 20px);
  height: 3px;
}

CSSはあくまで例なので、表示位置やデザインは好みに合わせて調整してください。

JavaScript

(function(window, document) {
  const selector = '.post-body'; // 処理対象セレクタ
  const elm = document.querySelector(selector);
  const pb = document.querySelector('.progress-bar');
  let goal = elm.clientHeight;
  update();

  function update() {
    let read = window.innerHeight - elm.getBoundingClientRect().top;
    let progress = Math.floor(read / goal * 100);
    if (read < 0) {
      progress = 0;
    } else if (read > goal) {
      progress = 100;
    }
    pb.style.background = 'linear-gradient(to right, tomato 0% ' + progress + '%, #d9d9d9 ' + progress + '% 100%)';
  }

  window.addEventListener('scroll', () => {
    update();
  });
})(window, document);

selector部分が処理対象となる記事本体要素のセレクタです。お使いのサイトやブログに応じて変更してください。

パイチャート型

円グラフ型の読了率メーターです。表示が円形でわかりやすいのと、読了率がセンターに表示されているのが特徴。

See the Pen Progress Circle by Fuma (@fuma_it) on CodePen.

こちらもバー型と同様、記事を読んだ量に応じて表示が変化します。

デモは読了率メーターと記事本体が重なってしまって少し見づらいので、実際に設置される場合は干渉しないように工夫して頂けたらと思います。

ソースコード

HTML

<div class="progress-circle">
  <div class="progress-circle-inner"></div>
</div>

CSS

.progress-circle {
  position: fixed;
  top: 30px;
  left: 30px;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100px;
  height: 100px;
  background-image: conic-gradient(tomato 0% 0%, #d9d9d9 0% 100%);
  border-radius: 50%;
  margin: 0 auto;
}
.progress-circle-inner {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 80%;
  height: 80%;
  font-size: 16px;
  font-weight: 700;
  background: #fff;
  border-radius: 50%;
}

パイチャートのCSSについては以下の記事を参考にさせて頂きました。

JavaScript

(function(window, document) {
  const selector = '.post-body'; // 処理対象セレクタ
  const elm = document.querySelector(selector);
  const pc = document.querySelector('.progress-circle');
  const pc_inner = pc.querySelector('.progress-circle-inner');
  let goal = elm.clientHeight;
  update();

  function update() {
    let read = window.innerHeight - elm.getBoundingClientRect().top;
    let progress = Math.floor(read / goal * 100);
    if (read < 0) {
      progress = 0;
    } else if (read > goal) {
      progress = 100;
    }
    pc_inner.innerText = progress + '%';
    pc.style.backgroundImage = 'conic-gradient(tomato 0% ' + progress + '%, #d9d9d9 ' + progress + '% 100%)';
  }

  window.addEventListener('scroll', () => {
    update();
  });
})(window, document);

ソースコード解説

今回のソースコードの肝は、記事を読んだ量(対象部分の表示量)の計算です。まず、基準となる記事本体の高さを以下のコードで取得します。

let goal = elm.clientHeight;

次に必要となるのが、対象部分の表示量read。このreadがわかれば、記事全体に対する進捗度progressを以下のように求めることができます。

let progress = Math.floor(read / goal * 100);

さて、ここで問題になるのがreadの求め方です。readは「現在のスクロール位置までに通過した表示量+ウィンドウの縦幅」で求めることができ、以下の図のような関係になります。

解説図1

scrollYは縦方向のスクロール位置、elemTopは要素上部の絶対位置です。

これを変形することで、readは以下のように表すことができます。

let read = window.innerHeight - elm.getBoundingClientRect().top;

後は求まったprogressをもとに、スクロール時に読了率メーターのCSSを動的に変化させることで、望み通りの表示を得ることができます。

コメントはまだありません