スクロールして表示領域(=ビューポート)に入ったらアニメーションされるプログレスバーを作ってみましょう。

インジケーター部分はプログレスバーからtransform: translate;であらかじめはみ出させておいて、ビューポートに入ったときにclassを付与して元の位置に戻します。

プログレスバーは本来<progress>というHTMLタグを使いますが、<progress>はブラウザごとにスタイルのリセットが必要なため面倒くさいので今回は使用していません。

サンプル

サンプルを見てみましょう。

See the Pen
アニメーションするプログレスバーを作る
by イロイロデザインラボラトリ (@iroirodesignlab)
on CodePen.

スクロールしてビューポートに入ったら、インジケーターと数字のテキストがアニメーションしながら表示されます。

どちらもアニメーションにCSSのtransitionを使用していて、インジケーターは1秒、数字のテキストは1.5秒かけてアニメーションするように時間をずらしています。

HTML

HTMLはこんな感じです。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title></title>
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <section>
      <div class="container">
        <div class="progress-list">
          <div class="progress-detail bar-blue"></div>
          <span class="score" data-score="83"></span>
        </div>

        <div class="progress-list">
          <div class="progress-detail bar-red"></div>
          <span class="score" data-score="65"></span>
        </div>

        <div class="progress-list">
          <div class="progress-detail bar-yellow"></div>
          <span class="score" data-score="79"></span>
        </div>
      </div>
    </section>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
    <script>
      $(document).ready(function () {
        function addClassInViewport() {
          $(".progress-detail").each(function () {
            var elementTop = $(this).offset().top;
            var elementBottom = elementTop + $(this).outerHeight();
            var viewportTop = $(window).scrollTop();
            var viewportBottom = viewportTop + $(window).height();

            if (elementBottom > viewportTop && elementTop < viewportBottom) {
              $(".progress-detail").addClass("progress-detail-active");
              $(".score").addClass("score-active");
            } else {
              $(".progress-detail").removeClass("progress-detail-active");
              $(".score").removeClass("score-active");
            }
          });
        }

        $(window).scroll(function () {
          addClassInViewport();
        });
      });
    </script>
  </body>
</html>

下記の部分を繰り返すことでプログレスバーを増やすことができます。

<div class="progress-list">
  <div class="progress-detail bar-blue"></div>
  <span class="score" data-score="83"></span>
</div>

bar-blueとdata-score=”83″は適宜変更が必要です。

class=”progress-detail bar-blue”

progress-detailはインジケーター部分です。bar-blueというclassが付いていますが、このclassでプログレスバーの長さと色を指定します。

data-score=”83″

値はプログレスバーに重なって表示される数字で、プログレスバーの長さと同じ値にしておきましょう。この値を変更してもプログレスバーの長さは変わりません。

CSS

CSSはSCSSを使っています。

.progress-list {
  max-width: 90%;
  margin: 1rem 5%;
  width: 20rem;
  height: 2rem;
  border: 1px solid #cccccc;
  background: #eeeeee;
  border-radius: 2rem;
  overflow: hidden; // はみ出した要素を非表示にする。
  position: relative;
}

.progress-detail {
  transform: translate(-100%, 0);
  transition: all 1s cubic-bezier(0.01, 0.99, 1, 1);
}

.progress-detail-active {
  transform: translate(0);
  height: 100%;
}

.bar-blue {
  background: #30336b;
  width: 83%;
}

.bar-red {
  background: #eb4d4b;
  width: 65%;
}

.bar-yellow {
  background: #f9ca24;
  width: 79%;
}

.score {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, 0%);
  opacity: 0;
  transition: all 1.5s cubic-bezier(0.01, 0.99, 1, 1);

  &:before {
    content: "" attr(data-score) " / 100";
    color: #ffffff;
  }
}

.score-active {
  transform: translate(-50%, -50%);
  opacity: 1;
}

.progress-list

プログレスバーです。インジケーターはあらかじめはみ出させておくので、overflow: hidden;を指定して、はみ出した要素が表示されないようにしておきます。

また、position: relative;は数字のテキストの位置をposition: absolute;で行いため、相対位置を決めるために指定しておきます。

.progress-detail

インジケーターです。transform: translate(-100%, 0);で左側にはみ出させておきます。
アニメーションにはtransitionを使用してcubic-bezierでニュアンスのある動きを付けます。

transition: all 1s cubic-bezier(0.01, 0.99, 1, 1);

cubic-bezierはジェネレーターを使うと時短になるのでおすすめです。

.progress-detail-active

ビューポートに入るとjQueryで付与されるclassです。transform: translate(0);にすることでインジケーターの位置を元に戻します。

.bar-blue

bar-から始まるclassはインジケーターの色と長さを指定します。インジケーターの長さを80%にする場合は、widthに80%と指定しておきます。

width: 80%;

プログレスバーを設置する数だけ増やす必要があります。

.score

数字のテキスト部分です。.progress-detailと同じように位置をずらして、さらにopacity: 0;で透明にしておきます。

数字を動的にカウントアップさせる場合はjquery-numerator:などカウントアップさせるためのプラグインがあるので、そちらを利用することで実装が可能です。

実装には下記の記事が参考になります。

&:before

contentにattr(data-score)とすることでHTMLのdata-score=”83″の部分を表示させます。

.score-active

ビューポートに入るとjQueryで付与されるclassです。transformの位置が戻り、opacity: 1;を指定することで透明だった要素が表示されるようにします。

JavaScript

JavaScriptはjQueryを使っています。

$(document).ready(function () {
  function addClassInViewport() {
    $(".progress-detail").each(function () {
      var elementTop = $(this).offset().top;
      var elementBottom = elementTop + $(this).outerHeight();
      var viewportTop = $(window).scrollTop();
      var viewportBottom = viewportTop + $(window).height();

      if (elementBottom > viewportTop && elementTop < viewportBottom) {
        $(".progress-detail").addClass("progress-detail-active");
        $(".score").addClass("score-active");
      } else {
        $(".progress-detail").removeClass("progress-detail-active");
        $(".score").removeClass("score-active");
      }
    });
  }

  $(window).scroll(function () {
    addClassInViewport();
  });
});

ビューポートに入ったらprogress-detailにprogress-detail-activeを付与、さらにscoreにscore-activeを付与、ビューポートから出るとそれぞれのclassが削除されます。