本文へスキップ
HELLO!
HIGASHIYUKI®.COM
0   /   100
ローディング

ローディング中に 12 本の線が順に濃くなるスピナー

デモ

仕様

動作
DOM
種類
レシピ
動作確認
three 0.186.0・gsap 3.15.0
公開日

しくみと調整

12本の線を 30° ずつ放射状に並べ、図形そのものは回さずに、線ごとの濃さを順にトゥイーンして回っているように見せます。濃さは CSS の opacity ではなく SVG の stroke-opacity で変えていて、要素ごとに合成レイヤーを作らず、線の中だけで描き直されます。

1周は 1 秒で、線どうしは 12 分の 1 周期ずつ遅れて薄れるので、途切れずに尾を引いて見えます。開始した直後から一部の線しか回っていない立ち上がりは避けたいので、最初から1周先の状態を描いています。

薄れ方には power1.out を使っていて、濃い状態を少し保ってから素早く薄れます。等速で薄れさせると、先頭の線がぼやけて読みにくくなります。

線の本数や太さ、色を変えるだけで、大きさの違う場所に使い回せます。投稿中の画面中央の表示や、押したボタンの中の小さな表示など、複数の場所で同じ部品を使えます。

コード

HTML
<div class="segment-loader" role="status">
  <svg class="segment-loader__svg" viewBox="-50 -50 100 100" aria-hidden="true"></svg>
  <span class="segment-loader__text">読み込み中</span>
</div>

CSS
.segment-loader {
  --ground: #f2f1ed; /* 地の色 */
  --ink: #262626;    /* 線の色 */
  --size: 96px;      /* SVG の一辺 */
  --width: 8;        /* 線の太さ(viewBox 100 の中の値) */
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  background: var(--ground);
}

.segment-loader__svg {
  display: block;
  width: var(--size);
  height: var(--size);
  overflow: visible;
}

.segment-loader__svg line {
  stroke: var(--ink);
  stroke-width: var(--width);
  stroke-linecap: round;
}

/* 読み上げにだけ届く文言。画面には出さない */
.segment-loader__text {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

JavaScript
import { gsap } from "gsap";

const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const SVG_NS = "http://www.w3.org/2000/svg";
const COUNT = 12;
const PERIOD = 1; // 1 周の時間(秒)
const MIN = reduceMotion ? 0.55 : 0.16; // いちばん薄い線の濃さ。動きを減らす設定では濃淡の差を小さくする

function createLoader(root) {
  const svg = root.querySelector(".segment-loader__svg");
  // 内側の半径 24・外側の半径 42 の線を 30° ずつ並べる(丸い線端のぶん外へ半分の太さだけはみ出す)
  const segments = Array.from({ length: COUNT }, (_, i) => {
    const line = document.createElementNS(SVG_NS, "line");
    line.setAttribute("x1", "0");
    line.setAttribute("y1", "-24");
    line.setAttribute("x2", "0");
    line.setAttribute("y2", "-42");
    line.setAttribute("transform", `rotate(${i * (360 / COUNT)})`);
    svg.append(line);
    return line;
  });

  // 図形は回さず、線ごとの濃さを 1 → MIN へ順にトゥイーンして、回っているように見せる
  const spin = gsap.fromTo(
    segments,
    { attr: { "stroke-opacity": 1 } },
    { attr: { "stroke-opacity": MIN }, duration: PERIOD, ease: "power1.out", stagger: { each: PERIOD / COUNT, repeat: -1 } }
  );
  spin.time(PERIOD); // 立ち上がりを飛ばし、全部の線が回っている状態から始める

  return {
    // 読み込みが終わったら呼ぶ
    done() {
      gsap.to(root, {
        autoAlpha: 0,
        duration: reduceMotion ? 0 : 0.3,
        onComplete: () => {
          spin.kill();
          root.remove();
        },
      });
    },
  };
}

// 例: 投稿中の HUD や、押したボタンの中にも同じ形で使える。終わったら loader.done()
export const loader = createLoader(document.querySelector(".segment-loader"));

インタラクションデザインについて、
お気軽にご相談ください。

ここに並べているのは、Web サイトの中で動く UI のサンプルです。新しく作るサイトに組み込むことも、いま公開中のサイトに、ボタンやヒーロー、スクロールの演出だけを足すこともできます。設計から実装までの進め方はWeb サイト制作、端末ごとの見え方や表示速度の考え方はレスポンシブウェブサイトのページにまとめています。映像として作る動きをお探しの場合はモーショングラフィックスをご覧ください。