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

ローディング中に四角が 3D で裏返り続ける

デモ

仕様

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

しくみと調整

四角いタイルを、縦の軸で半回転させたあと、続けて横の軸で半回転させます。どちらも半回転しか使わないので、表と裏の2面だけで裏返る動きを表せます。

1回裏返るのに 0.675 秒、裏返ったあとの間は 0.135 秒です。見る角度を正面から少し傾けているのは、真正面だと止まっている間はただの四角にしか見えないためです。

奥行きは親の要素に perspective、タイルと面には3D変形を保つ指定を添えます。裏の面は backface-visibility で隠しますが、表と裏の四隅の丸みをそろえておかないと、裏返ったときに向きが変わって見えます。

表と裏の色の差を大きくするほど、裏返る動きが読みやすくなります。読み込み中の全画面表示に向いていて、色や大きさを変えるだけでブランドに合わせられます。

コード

HTML
<div class="flip-loader" role="status">
  <div class="flip-loader__view">
    <div class="flip-loader__tile">
      <div class="flip-loader__face flip-loader__face--front"></div>
      <div class="flip-loader__face flip-loader__face--back"></div>
    </div>
  </div>
  <span class="flip-loader__text">読み込み中</span>
</div>

CSS
.flip-loader {
  --ground: #f2f1ed; /* 地の色 */
  --front: #A28767;  /* 表(中身の色。グレージュ) */
  --back: #262626;   /* 裏 */
  --size: 96px;       /* タイルの一辺 */
  --radius: 14px;     /* 角の丸み */
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  background: var(--ground);
  perspective: calc(var(--size) * 5);
}

.flip-loader__view {
  transform-style: preserve-3d;
  transform: rotateX(26deg) rotateY(-24deg);
}

.flip-loader__tile {
  position: relative;
  width: var(--size);
  height: var(--size);
  transform-style: preserve-3d;
}

.flip-loader__face {
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
  backface-visibility: hidden;
}

.flip-loader__face--front { background: var(--front); }
.flip-loader__face--back { background: var(--back); transform: rotateY(180deg); }

/* 読み上げにだけ届く文言。画面には出さない */
.flip-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 FLIP = 0.675; // 1 回裏返る時間(秒)
const GAP = 0.135; // 裏返ったあとに止まる時間(秒)

function createLoader(root) {
  const tile = root.querySelector(".flip-loader__tile");
  let tl;

  if (reduceMotion) {
    // 動きを減らす設定では裏返さず、明るさだけをゆっくり点滅させる
    tl = gsap.to(root, { opacity: 0.6, duration: 0.9, ease: "sine.inOut", repeat: -1, yoyo: true });
  } else {
    // rotationX を -180 → 続けて rotationY を -180。点対称の四角なので 1 ループで元に戻る
    tl = gsap
      .timeline({ repeat: -1, repeatDelay: GAP })
      .fromTo(tile, { rotationX: 0, rotationY: 0 }, { rotationX: -180, duration: FLIP, ease: "power2.inOut" })
      .fromTo(tile, { rotationY: 0 }, { rotationY: -180, duration: FLIP, ease: "power2.inOut" }, `+=${GAP}`);
  }

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

// 例: 読み込み中の全画面表示に。終わったら loader.done()
export const loader = createLoader(document.querySelector(".flip-loader"));

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

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