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

ローディング中に円弧が伸び縮みしながら回る

デモ

仕様

動作
DOM
種類
レシピ
使うもの
DrawSVGPlugin
動作確認
three 0.186.0・gsap 3.15.0
公開日

しくみと調整

1本の弧を伸ばしたり縮めたりしながら回し続けます。長さの操作には DrawSVGPlugin を使っていて、円周を測らずに % の長さで弧の始まりと終わりを指定できます。

弧はいちばん縮んだときに全周の 8% 残るようにしていて、伸びる半分と縮む半分で power1.inOut のイージングをかけています。回転には、弧が伸縮するあいだに先へ進む分(28.8°)を足しているので、1周ごとに継ぎ目が出ません。

回転の軸は svgOrigin で円の中心に固定しています。既定の軸のままだと、弧の見える長さが変わるたびに軸がわずかにずれます。

輪の大きさや色、1周の時間を変えるだけで、送信中のボタンや読み込み中の全画面表示など、いろいろな場所で使い回せます。

コード

HTML
<div class="spin-loader" role="status">
  <svg class="spin-loader__svg" viewBox="0 0 100 100" aria-hidden="true">
    <circle class="spin-loader__track" cx="50" cy="50" r="40"></circle>
    <circle class="spin-loader__arc" cx="50" cy="50" r="40"></circle>
  </svg>
  <span class="spin-loader__text">読み込み中</span>
</div>

CSS
.spin-loader {
  --ground: #f2f1ed;               /* 地の色 */
  --line: rgba(38, 38, 38, 0.12);  /* 溝の色 */
  --arc: #b27771;                  /* 弧の色(中身の色。くすみローズ) */
  --size: 144px;                   /* 輪の大きさ */
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  background: var(--ground);
}

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

.spin-loader__track,
.spin-loader__arc {
  fill: none;
  stroke-width: 9;
  stroke-linecap: round;
}

.spin-loader__track { stroke: var(--line); }
.spin-loader__arc { stroke: var(--arc); }

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

JavaScript
import { gsap } from "gsap";
import { DrawSVGPlugin } from "gsap/DrawSVGPlugin";

gsap.registerPlugin(DrawSVGPlugin);

const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const DURATION = 1.4; // 1 周の時間(秒)
const SEGMENT = 8; // 縮みきったときの弧の長さ(%)

function createLoader(root) {
  const arc = root.querySelector(".spin-loader__arc");
  let tl;

  if (reduceMotion) {
    // 動きを減らす設定では回さず、明るさだけをゆっくり点滅させる
    gsap.set(arc, { drawSVG: "0% 30%" });
    tl = gsap.to(root, { opacity: 0.5, duration: 0.9, ease: "sine.inOut", repeat: -1, yoyo: true });
  } else {
    // 弧は 8% ぶん先へ進んで縮むので、回転に 8% 分(28.8°)を足すと 1 周で継ぎ目が出ない
    tl = gsap
      .timeline({ repeat: -1 })
      .fromTo(arc, { rotation: -90, svgOrigin: "50 50" }, { rotation: -90 + 360 + SEGMENT * 3.6, svgOrigin: "50 50", duration: DURATION, ease: "none" }, 0)
      .fromTo(arc, { drawSVG: `0% ${SEGMENT}%` }, { drawSVG: "0% 78%", duration: DURATION / 2, ease: "power1.inOut" }, 0)
      .to(arc, { drawSVG: `${100 - SEGMENT}% 100%`, duration: DURATION / 2, ease: "power1.inOut" }, DURATION / 2);
  }

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

// 例: 送信中のボタンや、読み込み中の全画面表示に。終わったら loader.done()
export const loader = createLoader(document.querySelector(".spin-loader"));

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

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