デモ
デモを読み込めませんでした。
仕様
- 用途
- ローディング
- 動作
- DOM
- 種類
- レシピ
- 動作確認
- three 0.186.0・gsap 3.15.0
- 公開日
しくみと調整
3つのドットが左から順に跳ね、跳ねているあいだだけ色が変わります。小さなドットでも、いまどれが動いているかを目で追えるように、色の変化を足しました。右端が下りきると少し間を置いて、また左から始まります。
上がる動きは0.3秒の power2.out で、隣とは0.12秒ずつずらしています。上りを減速の曲線にしておくと、折り返した下りは加速の曲線になるので、ドットが落ちて着地したように見えます。次の周までの間は0.46秒。ここを詰めると、待っている人を急かすような速さになります。
往復の指定(yoyo と repeat)は、必ず stagger の中に書きます。外に書くと全体がひとつの動きとして往復するので、全部が順に上がりきってから、今度は逆の順に下りてきます。順番に跳ねて戻る形は、そこで崩れてしまいます。
読み込みが終わったときに止める関数も、最初から用意してあります。繰り返しを止めて要素ごと取り除くので、読み込みが済んだのにドットだけ跳ね続けることはありません。送信や一覧の読み込みのように終わりが読めない待ち時間のためのローダーで、受け取った量を数えられる処理なら、進み具合を表示するほうが待つ側は楽です。
コード
<div class="dots-loader" role="status">
<span class="dots-loader__dot"></span>
<span class="dots-loader__dot"></span>
<span class="dots-loader__dot"></span>
<span class="dots-loader__text">読み込み中</span>
</div>
.dots-loader {
--dot: 14px; /* 点の直径 */
--gap: 10px; /* 点の間 */
--ink: #262626; /* 静止している点の色 */
--jump: #b27771; /* 跳ねているあいだの点の色 */
--ground: #f2f1ed; /* 背景色 */
position: fixed;
inset: 0;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
gap: var(--gap);
background: var(--ground);
}
.dots-loader__dot {
width: var(--dot);
height: var(--dot);
border-radius: 50%;
background: var(--ink);
}
/* スクリーンリーダーにだけ読まれる文言。画面には表示しない */
.dots-loader__text {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
import { gsap } from "gsap";
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
function createLoader(root) {
const dots = root.querySelectorAll(".dots-loader__dot");
// 色と大きさは CSS で決める。GSAP は var(--name) のままでは補間できないので、値を読み取って渡す
const jump = getComputedStyle(root).getPropertyValue("--jump").trim();
const lift = dots[0].offsetHeight * 1.3;
// yoyo と repeat は stagger の中に書く(外に書くと、3 つがまとめて往復する)。
// repeatDelay は、跳ね終わってから次の周が始まるまでの間
const bounce = gsap.timeline({ repeat: -1, repeatDelay: 0.46 }).to(dots, {
y: reduceMotion ? 0 : -lift, // 動きを減らす設定では、色の変化だけで順番を見せる
backgroundColor: jump,
duration: 0.3,
ease: "power2.out",
stagger: { each: 0.12, yoyo: true, repeat: 1 },
});
return {
// 読み込みが終わったら呼ぶ。呼ぶまで跳ね続ける
done() {
gsap.to(root, {
autoAlpha: 0,
duration: reduceMotion ? 0 : 0.3,
onComplete: () => {
bounce.kill();
root.remove();
},
});
},
};
}
// 例: fetch や画像の読み込みを待って loader.done()
export const loader = createLoader(document.querySelector(".dots-loader"));