数字カウントアップ × 背景フェードのローディングアニメーション
0%から100%まで数字がカウントアップし、左から右へ現れた背景とともに消えていくローディングアニメーションの実装方法について解説しています。
JSのライブラリなどを使わないシンプルな構成で、CSS変数とclip-path、mix-blend-modeを組み合わせて実現しています。
実装例
早速ですが下記が実装例です。
ページ読み込み後に数字がカウントアップし、背景が左から塗りつぶされてフェードアウトしていきます。もう一度実行したい場合は、右下にある「Return」ボタンをクリックしてください。
シンプルながらも、擬似的な読み込みの進捗が視覚的にわかり、ページの読み込みが完了したことが伝わりやすいアニメーションだと思います。ライブラリ不要で軽量に実装できるアニメーションです。
解説
HTML/CSSとJavaScriptの処理を順を追って解説していきます。
HTMLの構造
HTMLは次のとおりです。
<div class="c-loading" data-loading="container"> <div class="c-loading__inner"> <div class="c-loading__bg" data-loading="bg"></div> <span class="c-loading__text" data-loading="text"> 0% </span> </div></div>c-loadingがローディング画面全体、c-loading__bgが塗りつぶし用の背景層、c-loading__textがパーセンテージ表示です。動かしたい要素にはdata-loading属性を付与し、JavaScriptから取得できるようにしています。
CSSのポイント
CSSは次のとおりです。
.c-loading { position: fixed; inset: 0; height: 100%; width: 100%; opacity: 1; z-index: 10; pointer-events: auto;}
.c-loading__inner { width: 100%; height: 100%; background-color: #fff;}
.c-loading__bg { --_clip-right: 100%; width: 100%; height: 100%; background-color: #1e3a5f; clip-path: inset(0 var(--_clip-right) 0 0); transition-timing-function: ease-in;}
.c-loading__text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; mix-blend-mode: difference; text-align: center; font-weight: 500; font-size: 20px; font-family: "Montserrat", sans-serif; letter-spacing: 0.05em;}
.c-loading--complete { opacity: 0; visibility: hidden; transition: all 0.6s ease; transition-delay: 0.6s; pointer-events: none;}c-loading__bgの初期状態は--_clip-right: 100%(clip-path: inset(0 100% 0 0))で、画面全体が塗りつぶされていない状態です。JavaScriptから--_clip-rightを更新することで、右側の切り取り幅を減らし、左から右へ塗りつぶし幅を制御しています。
c-loading__textにはmix-blend-mode: differenceを指定しています。これにより背景色が変わっても数字が常に見やすい色で表示されます。
100%に到達するとc-loading--completeクラスが付与され、ローディング画面全体がフェードアウトして操作を受け付けなくなります。
JavaScriptの処理を言語化
JavaScriptのアニメーションに関するコードは以下のとおりです。
const setProgress = () => { const container = document.querySelector("[data-loading='container']");
if (!container) return;
const bg = container.querySelector("[data-loading='bg']"); const text = container.querySelector("[data-loading='text']");
if (!bg || !text) return;
let current = 0;
const updateProgress = () => { current += (100 - current) * 0.075;
if (current > 99.9) { current = 100; }
bg.style.setProperty("--_clip-right", `${100 - current}%`); text.textContent = Math.floor(current) + "%";
if (current < 100) { requestAnimationFrame(updateProgress); } else { container.classList.add("c-loading--complete"); } };
requestAnimationFrame(updateProgress);};
setProgress();requestAnimationFrameでフレームごとに進捗を更新しています。current += (100 - current) * 0.075の計算により、最初は速く、終盤はゆっくりと増えるイージングがかかります。
フレームごとに更新されるcurrentの値を、以下の2つに反映しています。
- 背景の塗りつぶし幅:
--_clip-right(100 - current%)を更新 - 表示する数字:
Math.floor(current)で表示する数字を整数にし、textContentで更新
1つの値を基準にすることで、数字と背景の塗りつぶし幅が同期しています。最終的にcurrentが100%に到達したら.c-loading--completeクラスを付与してローディング画面を非表示にします。
表現アレンジのアイデア
今回の構成はシンプルなため、いくつかの値や要素を変えるだけで表現の幅を広げられます。
- 背景が出現する方向を変える:
clip-pathのinsetで切り取る辺を変えれば、右から・上から・下からなど任意の方向へ背景を出現させられます。CSS変数で制御する辺を切り替えるだけで、同じJavaScriptの進捗ロジックを流用できます。 - イージングの係数を調整する:JavaScriptの
current += (100 - current) * 0.075にある0.075は、1フレームごとに残り進捗の何%分を進めるかを決める係数です。値を大きくすると素早く100%に到達し、小さくするとゆったりした印象になります。サイトのトーンに合わせて調整できます。 - カウントアップをプログレスバーに変更する:中央の数字の代わりに、横棒のバーを置き、
scaleXを進捗率に合わせて伸ばす形にもできます。数字とバーを併用すれば、視覚と数値の両方で進捗を伝えられます。
いずれも今回の仕組みをベースに実装できるため、まずはデモの形を再現し、必要に応じて調整していくのが手軽かと思います。
さらにユーザー体験を高めるには
今回のデモでは省略していますが、次のような処理を加えるとユーザー体験がさらに良くなるでしょう。
- 画像の
loadイベントなどを監視し、実際の読み込み状況と進捗を同期させる localStorageを使用し、初回訪問時のみローディングを表示する
前者は、デモのように時間ベースで進捗を進めるより、ページ内のリソースが実際に読み込まれたタイミングと連動するため、体感のズレを抑えられます。後者は、2回目以降の訪問でローディングをスキップできるため、リピーターにとっての待ち時間を減らせます。
あわせて読みたい
