パーセントの数値とゲージがリアルタイムで読み込まれていくローディング画面をJavaScriptで実装してみる(備忘録)

今回はJavaScriptを用いて、パーセントの数値とゲージがリアルタイムで読み込まれていくローディング画面を実装してみる。

デモは下記を参考いただきたいが、少し容量の大きめの画像を入れているので、通信制限を気にする方は気をつけていただければと思う。

それでは解説に入っていく。

htmlの記述について

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="robots" content="index, follow" />
  <title>ローディング</title>
  <link rel="stylesheet" href="../reset.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div id="bl_loading">
    <div id="bl_loadingPercentWrap"><span id="bl_loadingPercentNumber"></span>%</div>
    <div id="bl_loadingGaugeWrap"><span id="bl_loadingGaugeMeter"></span></div>
  </div>
  <main>
    <section>
      <div class="ly_inner" id="menu1">
        <h2>menu1</h2>
        <div class="md_imgblock">
          <p><img src="img_01.jpg" alt=""></p>
          <p><img src="img_02.jpg" alt=""></p>
          <p><img src="img_03.jpg" alt=""></p>
          <p><img src="img_04.jpg" alt=""></p>
          <p><img src="img_05.jpg" alt=""></p>
          <p><img src="img_06.jpg" alt=""></p>
          <p><img src="img_07.jpg" alt=""></p>
          <p><img src="img_08.jpg" alt=""></p>
          <p><img src="img_09.jpg" alt=""></p>
          <p><img src="img_10.jpg" alt=""></p>
          <p><img src="img_01.jpg" alt=""></p>
          <p><img src="img_02.jpg" alt=""></p>
          <p><img src="img_03.jpg" alt=""></p>
          <p><img src="img_04.jpg" alt=""></p>
          <p><img src="img_05.jpg" alt=""></p>
          <p><img src="img_06.jpg" alt=""></p>
          <p><img src="img_07.jpg" alt=""></p>
          <p><img src="img_08.jpg" alt=""></p>
          <p><img src="img_09.jpg" alt=""></p>
          <p><img src="img_10.jpg" alt=""></p>
          <p><img src="img_01.jpg" alt=""></p>
          <p><img src="img_02.jpg" alt=""></p>
          <p><img src="img_03.jpg" alt=""></p>
          <p><img src="img_04.jpg" alt=""></p>
          <p><img src="img_05.jpg" alt=""></p>
          <p><img src="img_06.jpg" alt=""></p>
          <p><img src="img_07.jpg" alt=""></p>
          <p><img src="img_08.jpg" alt=""></p>
          <p><img src="img_09.jpg" alt=""></p>
          <p><img src="img_10.jpg" alt=""></p>
          <p><img src="img_01.jpg" alt=""></p>
          <p><img src="img_02.jpg" alt=""></p>
          <p><img src="img_03.jpg" alt=""></p>
          <p><img src="img_04.jpg" alt=""></p>
          <p><img src="img_05.jpg" alt=""></p>
          <p><img src="img_06.jpg" alt=""></p>
          <p><img src="img_07.jpg" alt=""></p>
          <p><img src="img_08.jpg" alt=""></p>
          <p><img src="img_09.jpg" alt=""></p>
          <p><img src="img_10.jpg" alt=""></p>
          <p><img src="img_01.jpg" alt=""></p>
          <p><img src="img_02.jpg" alt=""></p>
          <p><img src="img_03.jpg" alt=""></p>
          <p><img src="img_04.jpg" alt=""></p>
          <p><img src="img_05.jpg" alt=""></p>
          <p><img src="img_06.jpg" alt=""></p>
          <p><img src="img_07.jpg" alt=""></p>
          <p><img src="img_08.jpg" alt=""></p>
          <p><img src="img_09.jpg" alt=""></p>
          <p><img src="img_10.jpg" alt=""></p>
          <p><img src="img_01.jpg" alt=""></p>
          <p><img src="img_02.jpg" alt=""></p>
          <p><img src="img_03.jpg" alt=""></p>
          <p><img src="img_04.jpg" alt=""></p>
          <p><img src="img_05.jpg" alt=""></p>
          <p><img src="img_06.jpg" alt=""></p>
          <p><img src="img_07.jpg" alt=""></p>
          <p><img src="img_08.jpg" alt=""></p>
          <p><img src="img_09.jpg" alt=""></p>
          <p><img src="img_10.jpg" alt=""></p>
          <p><img src="img_01.jpg" alt=""></p>
          <p><img src="img_02.jpg" alt=""></p>
          <p><img src="img_03.jpg" alt=""></p>
          <p><img src="img_04.jpg" alt=""></p>
          <p><img src="img_05.jpg" alt=""></p>
          <p><img src="img_06.jpg" alt=""></p>
          <p><img src="img_07.jpg" alt=""></p>
          <p><img src="img_08.jpg" alt=""></p>
          <p><img src="img_09.jpg" alt=""></p>
          <p><img src="img_10.jpg" alt=""></p>
          <p><img src="img_01.jpg" alt=""></p>
          <p><img src="img_02.jpg" alt=""></p>
          <p><img src="img_03.jpg" alt=""></p>
          <p><img src="img_04.jpg" alt=""></p>
          <p><img src="img_05.jpg" alt=""></p>
          <p><img src="img_06.jpg" alt=""></p>
          <p><img src="img_07.jpg" alt=""></p>
          <p><img src="img_08.jpg" alt=""></p>
          <p><img src="img_09.jpg" alt=""></p>
          <p><img src="img_10.jpg" alt=""></p>
          <p><img src="img_01.jpg" alt=""></p>
          <p><img src="img_02.jpg" alt=""></p>
          <p><img src="img_03.jpg" alt=""></p>
          <p><img src="img_04.jpg" alt=""></p>
          <p><img src="img_05.jpg" alt=""></p>
          <p><img src="img_06.jpg" alt=""></p>
          <p><img src="img_07.jpg" alt=""></p>
          <p><img src="img_08.jpg" alt=""></p>
          <p><img src="img_09.jpg" alt=""></p>
          <p><img src="img_10.jpg" alt=""></p>
        </div>
      </div>
    </section>
  </main>
  <script src="style.js"></script>
</body>
</html>

まず、ローディング画面全体をbl_loadingで囲う。

その中のパーセント部分をbl_loadingPercentWrapで作成。パーセントの数字が入る部分はbl_loadingPercentNumberで作り、中身は空欄にしている。

横棒のゲージ部分はbl_loadingGaugeWrapで作成。あとはリアルタイムでゲージが伸びていく部分をbl_loadingGaugeMeterで作成すればOK。

<div id="bl_loading">
    <div id="bl_loadingPercentWrap"><span id="bl_loadingPercentNumber"></span>%</div>
    <div id="bl_loadingGaugeWrap"><span id="bl_loadingGaugeMeter"></span></div>
</div>

あとは画像を100枚ソースに当て込んで、検証することにする。

cssの記述について

@charset "utf-8";

/* ==========================
  初期設定
========================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}
img {
  width: 100%;
}

/* ==========================
  ローディング画面
========================== */
#bl_loading {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #444;
  color: #fff;
  font-size: 12px;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  transition: 0.3s;
}
#bl_loadingPercentWrap {
  margin-bottom: 20px;
}
#bl_loadingGaugeWrap {
  width: 100%;
  height: 3px;
  background-color: #666;
}
#bl_loadingGaugeMeter {
  width: 0;
  height: 3px;
  background-color: #fff;
  display: block;
}

/* ==========================
  コンテンツの中身
========================== */
.ly_inner {
  width: 100%;
  max-width: 1080px;
  margin: 100px auto;
  padding: 20px;
  background-color: #ccc;
}
.ly_inner h2 {
  font-size: 150%;
  font-weight: bold;
  margin-bottom: 30px;
}
.md_imgblock {
  text-align: center;
}
.md_imgblock > * + * {
  margin-top: 10px;
}

「●%」のテキストが表示される部分については、上下左右中央に配置されるようにすでに設定しているので、マージンを下側につけてあげてゲージとの余白を空けてあげるだけでOKだ。

横棒ゲージについては、ローディング画面全体よりも少し明るい色に設定し、リアルタイムで読み込まれていくゲージは、白に設定している。

#bl_loadingPercentWrap {
  margin-bottom: 20px;
}
#bl_loadingGaugeWrap {
  width: 100%;
  height: 3px;
  background-color: #666;
}
#bl_loadingGaugeMeter {
  width: 0;
  height: 3px;
  background-color: #fff;
  display: block;
}

JavaScriptの記述について

'use strict';

// ローディング画面
const images = document.getElementsByTagName('img'); // ページ内のimgタグを取得
const loadingArea = document.getElementById('bl_loading'); // ローディング画面全体
const percentNumber = document.getElementById('bl_loadingPercentNumber'); // ●%の●部分
const loadingGauge = document.getElementById('bl_loadingGaugeMeter'); // リアルタイムで読み込まれるゲージ部分
let imgCounting = 0;
let baseCounting = 0;
const gaugeWidth = 100; // ゲージの全体幅
let current;

// 画像の読み込み
for (let i = 0; i < images.length; i++) {
  const img = new Image(); // 新たなimg要素を作成
  // 画像読み込み完了したとき
  img.onload = function() {
    imgCounting += 1;
  }
  // 画像読み込み失敗したとき
  img.onerror = function() {
    imgCounting += 1;
  }
  img.src = images[i].src; // ソースのパスを設定
};

// setIntervalを使って一定時間ごとに処理を繰り返す
const nowLoading = setInterval(function () {
  // baseCountingがimgCountingより大きくならない条件の場合に処理を実行させる。2回目以降にページを読み込んだ時に画像の読み込み履歴が残っている関係で、ローディング画面の表示が速く終わってしまうため、その対策として条件をつけている。
  if(baseCounting <= imgCounting) {
    // リアルタイムで読み込んでいるパーセントを取得
    current = Math.floor(baseCounting / images.length * 100);
    // ●%の●部分に数字を置き換える
    percentNumber.innerHTML = current;
    // リアルタイムで読み込まれるゲージ部分を反映させる
    loadingGauge.style.width = Math.floor(gaugeWidth / 100 * current) + '%';
    baseCounting += 1;

    // 全て読み込んだ時
    if (baseCounting === images.length) {
      setTimeout(function() {
        // ローディング画面全体の非表示
        loadingArea.style.display = 'none';
        // ローディングの終了
        clearInterval(nowLoading);
      }, 300);
    }
  }
}, 50);

最初は変数を宣言して引っ張ってこれるようにする。何に対して指定しているのかはコメントアウトして後ろの方に記載しているので参考にしてみて欲しい。

後ろの方でletを使用しているのは、値の内容が変わっていく関係でletを指定している。

currentについては、後半でソースを入れる予定のため値を特に入れていない。

画像の読み込みについて

ページを読み込むときに、画像がどんどん読み込まれていくのだが、この画像が読み込まれている状況を把握するためにfor文を使って処理を書いていく。

for (let i = 0; i < images.length; i++) {
  const img = new Image(); // 新たなimg要素を作成
  // 画像読み込み完了したとき
  img.onload = function() {
    imgCounting += 1;
  }
  // 画像読み込み失敗したとき
  img.onerror = function() {
    imgCounting += 1;
  }
  img.src = images[i].src; // ソースのパスを設定
};

新たなimg要素を作成するためにnew Image()を使って、要素を作成していく。

この時に読み込みに成功したり失敗したりすることがあるので、どちらの場合でも、次の読み込みに進められるようonloadとonerrorを使ってカウントを1増やして、次のimgに移れるようにしている。

srcに数字が0、1、2…と入るようになる。

setIntervalを使って一定時間ごとにローディングの処理を繰り返す

const nowLoading = setInterval(function () {
  // baseCountingがimgCountingより大きくならない条件の場合に処理を実行させる。2回目以降にページを読み込んだ時に画像の読み込み履歴が残っている関係で、ローディング画面の表示が速く終わってしまうため、その対策として条件をつけている。
  if(baseCounting <= imgCounting) {
    // リアルタイムで読み込んでいるパーセントを取得
    current = Math.floor(baseCounting / images.length * 100);
    // ●%の●部分に数字を置き換える
    percentNumber.innerHTML = current;
    // リアルタイムで読み込まれるゲージ部分を反映させる
    loadingGauge.style.width = Math.floor(gaugeWidth / 100 * current) + '%';
    baseCounting += 1;

    // 全て読み込んだ時
    if (baseCounting === images.length) {
      setTimeout(function() {
        // ローディング画面全体の非表示
        loadingArea.style.display = 'none';
        // ローディングの終了
        clearInterval(nowLoading);
      }, 300);
    }
  }
}, 50);

setIntervalの時間設定

setIntervalは一定時間ごとに処理を繰り返すのだが、今回は最後の行の「50」という数字で0.05秒ごとに行うよう設定した。

今回は同じページを2回目以降に読み込んでいて、画像の履歴も残っているため速くローディング画面の表示が終わってしまうという状況にも対応できるようにした。

リアルタイムで読み込んでいるパーセントを取得する

ifでbaseCounting <= imgCountingを設定することで、baseCountingの数字がimgCountingより大きくならない条件下で発火できるようにしている。

currentにMath.floor(baseCounting / images.length * 100)を代入する。images.lengthは今回は100になるため、計算すると「1、2、3、4…」と値が入るようになる。Math.floorは小数点以下の数字を切り捨てるという意味になる。

この値がpercentNumber.innerHTMLに対してcurrentを指定することで、html側に数字が入るようになる。html側に数字を入れていなかったのはここで数字を入れたかったためだ。

続いて、横棒のゲージにも似たようなことをしなくてはならない。

リアルタイムで読み込まれるゲージ部分を反映させる

loadingGauge.style.widthでhtmlソースにCSSを反映させる。Math.floor(gaugeWidth / 100 * current) + ‘%’を指定することで、ゲージの全体幅を100等分してから現在の読み込んでいる数値をかけていくことになるので、ゲージ全体に対してどのくらいの割合まで読み込まれているかが計算できるようになり、それがページ上に反映されていくようになる。今回は%をつけないといけないので忘れないよう注意しよう。

あとはbaseCounting += 1によって、値は上限に達するまで繰り返し処理が行われる。

全て読み込んだ時の処理

if文でbaseCounting === images.lengthにより、数が完全一致した時にsetTimeoutを使って、ローディング画面全体の非表示とローディングの終了の処理をしている。

setTimeoutを使っているのは、ページのローディングが100パーセントまで来たときにすぐに消えないようにするためだ。これを指定しないと100パーセントに到達する段階ですぐにローディング画面が消えてしまう。状況に応じて使い分けていった方がよさそうである。

loadingArea.style.displayにnoneを指定することで、読み込み終了時にローディング画面が消え、clearInterval(nowLoading)を設定することで、setIntervalの繰り返し処理をストップさせることができる。

これで実装完了だ。