CSSとJavaScriptでローディングアニメーションを作る#01
A:ドット1つ・線なし
B:ドット1つ・線あり
C:ドット5つ・線あり
幅300pxの中を、10pxのドットが左右にランダムに行き来するローディングアニメーションを作る方法。HTMLの data- 属性を変えるだけで、ドットの数と背景の線の有無を切り替えられる。
動きは次のとおり。
- ドットは行き先も移動時間もランダムに決まり、止まらず動き続ける
- 動いている間だけ、ドットが上下に圧縮されて平らになる
- 左右の端に着いたときだけ、ドットがオレンジ色になる
- ドットが複数のときは、それぞれ別のタイミング・場所に動く
パターンは次の3つ。
| パターン | HTML |
|---|---|
| A:ドット1つ・線なし | data-dots="1" data-line="off" |
| B:ドット1つ・線あり | data-dots="1" data-line="on" |
| C:ドット5つ・線あり | data-dots="5" data-line="on" |
HTML(ドットはJavaScriptが自動で作るので、div は空のままにする)
<div class="walker" data-dots="1" data-line="off" role="status" aria-label="読み込み中"></div>.walker {
position: relative;
width: 300px; /* 動く範囲の幅。JSがこの幅を自動で読み取る */
height: 10px; /* ドットの高さと同じにする */
}
/* 背景の線(data-line="on" のときだけ表示) */
.walker.line::before {
content: "";
position: absolute;
left: 0;
right: 0;
top: 4.5px; /* ドットの縦中央 */
height: 1px;
background: #d9d9d9; /* 線の色 */
}
/* ドット */
.walker .dot {
position: absolute;
left: 0;
top: 0;
width: 10px; /* ドットの大きさ(直径) */
height: 10px;
border-radius: 50%;
background: #555555; /* ふだんの色 */
transition-property: translate, background-color;
transition-timing-function: cubic-bezier(.45, 0, .55, 1), ease-out;
transition-duration: 0s, .12s; /* 移動の時間はJSが毎回ランダムに決める */
}
/* 端に着いたときの色 */
.walker .dot.edge {
background: #ff8a00;
}(function () {
var DUR_MIN = 250; // 1回の移動の最短時間(ミリ秒)。小さいほど速い
var DUR_MAX = 700; // 1回の移動の最長時間(ミリ秒)
var EDGE_CHANCE = 0.35; // 端へ向かう確率(0〜1)
var EDGE_HOLD = 160; // 端に着いたあと、オレンジのまま止まる時間(ミリ秒)
var MIN_DISTANCE = 40; // 1回の移動の最短距離(px)
var PAUSE_MAX = 60; // 端以外で着いたあと、次へ動くまでの最大の間(ミリ秒)
var SQUASH = '1.35 0.6'; // 移動の真ん中での形(横の倍率 縦の倍率)
var reduce = window.matchMedia &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function run(el, dot) {
var size = dot.offsetWidth;
var x = Math.random() * (el.clientWidth - size);
dot.style.translate = x + 'px 0';
if (reduce) { return; }
function go() {
var maxX = el.clientWidth - size;
dot.classList.remove('edge');
var nx;
if (Math.random() < EDGE_CHANCE) {
nx = x > maxX / 2 ? 0 : maxX;
} else {
do {
nx = Math.random() * maxX;
} while (Math.abs(nx - x) < MIN_DISTANCE || nx < 1 || nx > maxX - 1);
}
var dur = DUR_MIN + Math.random() * (DUR_MAX - DUR_MIN);
dot.style.transitionDuration = dur + 'ms, .12s';
dot.style.translate = nx + 'px 0';
dot.animate(
[{ scale: '1 1' }, { scale: SQUASH }, { scale: '1 1' }],
{ duration: dur, easing: 'ease-in-out' }
);
x = nx;
var atEdge = (nx === 0 || nx === maxX);
setTimeout(function () {
if (atEdge) {
dot.classList.add('edge');
setTimeout(go, EDGE_HOLD);
} else {
setTimeout(go, Math.random() * PAUSE_MAX);
}
}, dur);
}
setTimeout(go, 100 + Math.random() * 400);
}
var list = document.querySelectorAll('.walker');
for (var i = 0; i < list.length; i++) {
var el = list[i];
var n = parseInt(el.getAttribute('data-dots'), 10) || 1;
if (el.getAttribute('data-line') === 'on') { el.classList.add('line'); }
for (var j = 0; j < n; j++) {
var dot = document.createElement('div');
dot.className = 'dot';
el.appendChild(dot);
run(el, dot);
}
}
})();調整するときの変更箇所は次のとおり。
| 変えたいもの | 変更する場所 |
|---|---|
| ドットの数 | HTML data-dots(好きな数でよい) |
| 線の表示 | HTML data-line(on / off) |
| 動く範囲の幅 | CSS .walker の width(JSの変更は不要) |
| ドットの色 | CSS .walker .dot の background |
| 端に着いたときの色 | CSS .walker .dot.edge の background |
| 全体の速さ | JS DUR_MIN / DUR_MAX |
| オレンジになる頻度 | JS EDGE_CHANCE |
| ドットの平らさ | JS SQUASH(縦の数字を小さくするほど平ら) |
ドットの大きさを変えるときは、CSSの .walker の height と、線の top も合わせて直す。1ページに複数置いても、それぞれ別の設定で動く。
translate / scale プロパティを使うため、2022年以降のChrome・Edge・Safari・Firefoxが必要。表示環境によって動きの見え方が異なる場合があるため、本番に反映する前に確認を推奨。
NOTICES
- 記事内容は実装させたものがほとんどですが自己責任で参考にしてください。