ページの読み込みをしているようなローディングのアニメーションをやりたい。
そんな要件はありますでしょうか?
ローディングと言っても、いろんなのがありますよ。
そうですね。
今回はあらかじめ、こんな感じにしたいとサンプルページを教えていただけました。
こんな感じのサンプル
https://codesandbox.io/p/sandbox/loading-text-animation-gnl3h
0%~100%の数値がカウントアップされています。
入力テキストにワイプアニメーション効果が現れる、凡庸性の高いサンプルです。すごい!
「このまま使えるぞ」と思ったのですが。。
目次
そのまま使うには難しかった
デザイン文字にはアウトラインが掛かっていて、その文字はデザイナーさんのこだわりカーニングが施されていることが判明です。

<span>A</span>とかで囲って。。テキスト間隔を。。
1文字づつ調整か?と思ったのですが、やる前から気が遠くなるの感じ、SVG画像にすることにした時の記事内容となります。
そんな時「mask-image」を教えてもらいました。
mask-imageを使う
マスク状態にあるsvgに、疑似要素の色を差し込むイメージでしょうか。
よくわかりません。
わかりやすかったサイトのURLを貼り付けます。
参考:「mask-image」でSVGアイコンの色をCSSで変えよう
https://zenn.dev/kagan/articles/cf3332462262f1
参考:雲隠れ
https://lopan.jp/css-mask/cloud-mask/
mask-imageって結構前から聞いたことがあるような。。
IE以外で使えるようですね。
【共有】ソースコード
サンプル作成したソースコードを共有します。
SVG版サンプル(オプション1バージョン)
https://astrowave.jp/amnesia_record/loding_anim.php
↑リンク先ページで更新ボタンすると何回もアニメーションを確認できます。
要件は以下です。
・ただの5秒固定のアニメーションであること(データloadチェックは不要)
・ロゴ文字を違う色でワイプさせたい
・プログレスバーがほしい
・数値%のカウントアップがほしい
html
<div id="load">
<div class="load_inner">
<div id="root"></div>
</div>
</div>
id=”root”のdivに、スクリプトが出力するソースが差し込まれる想定です。
css
#load {
width: 100%;
padding: 0;
position: relative; /* fixed */
top: 0;
left: 0;
opacity: 1;
visibility: visible;
transition: .3s;
box-sizing: border-box;
z-index: 2;
background: #F6F7F2;
}
.load_inner {
position: relative;
}
.flex-container {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
height: 100vh;
}
.progress-wrapper {
width: 560px;
height: 29px;
margin: 40px 0 0;
display: flex;
}
.progress-anim {
display: inline-block;
width: 100%;
height: 2px;
background-color: #ddd;
position: relative;
animation: loading 5s linear;
}
.progress-anim::after {
content: "";
position: absolute;
top: -10px;
right: 0;
width: 15px;
height: 29px;
background: #f6f7f2;
}
.box-text {
font-family: "Noto Sans JP", "Hiragino Kaku Gothic ProN", Meiryo, sans-serif;
font-weight: 500;
font-size: 14px;
line-height: 29px;
text-align: center;
color: #000;
width: 0;
margin: 0 0 0;
padding: 0 0 0 0;
position: relative;
transform: translateY(-50%) translateX(0%);
}
.box-progress {
width: 560px;
height: 58px;
background-color: #D0DEE6;
-webkit-mask-image: url(./img/logo_gray.svg);
mask-image: url(./img/logo_gray.svg);
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: contain;
mask-size: contain;
position: relative;
}
.box-progress::after {
-webkit-transform-origin: left center;
-ms-transform-origin: left center;
transform-origin: left center
}
.box-progress::after {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: #BB161D;
animation: loading 5s linear;
}
.fill-progress {
width: 0%;
height: 20px;
background-color: #BB161D;
transition: width 0.5s; /* アニメーション */
position: relative;
margin-right: 10px; /* 数字との間隔 */
}
@keyframes loading {
0% {
width: 0;
}
}
mask-imageが使われています。
ほかに重要そうなことは、
svg文字とプログレスバーのサイズを同じくらいの幅にすること。
ほか、5秒の指定が3箇所あります。
javascript
<script>
// loading
document.addEventListener("DOMContentLoaded", function() {
const root = document.getElementById('root');
const flexContainer = document.createElement('div');
flexContainer.className = 'flex-container';
const boxProgress = document.createElement('h1');
boxProgress.className = 'box-progress';
boxProgress.setAttribute('data-text', 'ASTROWAVE');
boxProgress.innerText = 'ASTROWAVE';
const boxWrapper = document.createElement('div');
boxWrapper.className = 'progress-wrapper';
const boxText = document.createElement('span');
boxText.className = 'box-text';
boxText.innerText = '0%';
const progressBarFill = document.createElement('span');
progressBarFill.className = 'progress-anim';
boxWrapper.appendChild(progressBarFill);
boxWrapper.appendChild(boxText);
flexContainer.appendChild(boxProgress);
flexContainer.appendChild(boxWrapper);
root.appendChild(flexContainer);
let startTime = null;
const totalDuration = 5000; // 5 seconds
function animateProgress(timestamp) {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
// Calculate percentage of completion
const progress = Math.min(elapsed / totalDuration, 1); // 0 to 1
const percentage = Math.floor(progress * 100);
// Update text and progress bar width
boxText.innerText = `${percentage}%`;
if (elapsed < totalDuration) {
requestAnimationFrame(animateProgress);
} else {
webStorage(); // Animation completed
}
}
requestAnimationFrame(animateProgress);
});
const webStorage = function () {
const loadElement = document.getElementById('load');
if (sessionStorage.getItem('visit')) {
loadElement.style.opacity = 0;
loadElement.style.visibility = 'hidden';
} else {
sessionStorage.setItem('visit', 'true');
setTimeout(() => {
loadElement.style.opacity = 0;
loadElement.style.visibility = 'hidden';
}, 5000); // 5秒に設定
}
}
// 初回訪問時、初期化される
document.addEventListener("DOMContentLoaded", function() {
webStorage();
});
</script>
rootのdivに、3種類の要素を吐き出し(appendChild)てほしいのでソースが長くなってしましました。
もう少しコード整理できそうですが、すみません。
解説はchatGPTなどに放り込んでくださいませ。
【オプション1】初回訪問でも再読み込みでも見せる
上の共有ソースコードは、ローディングを初回訪問で1回見たら、次は見なくていいよ、という想定でした。
そういう想定があるなら最初に言ってください。
すみません。
以下は、初回とか関係なく「必ずローディングを見せたい」バージョンです。
const webStorage = function () {
const loadElement = document.getElementById('load');
// 初回訪問かどうかを問わず、loadingアニメーションを表示
setTimeout(() => {
loadElement.style.opacity = 0;
loadElement.style.visibility = 'hidden';
}, 5000); // 5秒に設定
}
// 初回訪問でも再読み込みでも、初期化
document.addEventListener("DOMContentLoaded", function() {
webStorage();
});
webStorageのsessionStorage部分を削ります。
ローディングアニメーション画面を設置したい話は以上です。
【オプション2】ローディング後にswiperを合わせたい
ローディングが終わったらスライダーを表示させたい時はないですか?
あるような、ないような。
そんな時はswiperの初期化をfunctionで囲って関数で作成すると良いとAIに教えてもらいました。
javascript
function initializeSwiper() {
const mySwiper = new Swiper('.swiper', {
slidesPerView: 1,
spaceBetween: 0,
loop: true,
loopAdditionalSlides: 1,
speed: 1000,
autoplay: {
delay: 3000,
disableOnInteraction: false,
},
pagination: {
el: '.swiper-pagination',
clickable: true,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
on: {
init: function () {
this.slides.forEach((slide, index) => {
const formattedIndex = String(index + 1).padStart(2, '0');
slide.classList.add(`index_${formattedIndex}`);
});
},
},
});
}
その関数をwebStorageのところに差し込む形となります。
const webStorage = function () {
const loadElement = document.getElementById('load');
if (sessionStorage.getItem('visit')) {
loadElement.style.opacity = 0;
loadElement.style.visibility = 'hidden';
initializeSwiper(); // swiper
} else {
sessionStorage.setItem('visit', 'true');
setTimeout(() => {
loadElement.style.opacity = 0;
loadElement.style.visibility = 'hidden';
initializeSwiper(); // swiper
}, 5000); // 5秒に設定
}
}
webStorageのタイミングでswiper初期化しましょう。
【その他】cssでチラリ表示を回避する
ローディングはモーダルのようなfixedの見せ方ではないですか?
</body>の上にソースコードを仕込むと、先にあるソース内容のレンダリングが一瞬、ローディングよりも早く表示が見える時があるのではないでしょうか。
そんな時はopacityのanimationで逃げるのもいいと思います。
.swiper {
opacity: 0;
animation: showSwiper 0.2s ease forwards;
}
@keyframes showSwiper {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
これは別にこの記事に必要ないかなと思った。けど入れました。
【AI】イラストを描いてもらった
今回の記事のキャッチ画像で使わせてもらいます「Memeplex.app」で作成した画像です。誰でもgoogleアカウントでログインして使えます。
この記事にピッタリなイラストのための考えたリクエストは、「複数のシステムタスクがローディング中の円グラフと、アバター2名の人間の姿を出したい。」です。
選んだモードは以下の3つです。
- 特撮
- サイバーパンク風
- UnrealEngine5風

サイバーでパンクしている気がします。
参考サイト置き場
「mask-image」でSVGアイコンの色をCSSで変えよう!
https://zenn.dev/kagan/articles/cf3332462262f1
グラデーションもできる。
lopan:雲隠れ
https://lopan.jp/css-mask/cloud-mask/
lopanさんのサイト、かわいくて頻繁に見にいきます。
ワイプとは
https://kotobank.jp/word/%E3%83%AF%E3%82%A4%E3%83%97-10162