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

ローディングの長さの変わる帯が流れ続ける不定進捗バー

デモ

仕様

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

しくみと調整

ヘッダーの下の細い帯を、長さの変わる 2 本の帯が続けて流れていきます。進み具合がわからない読み込みを表す、よく知られた動きです。帯の左端と右端をそれぞれ動かして、伸びたり縮んだりしながら流れる形を作っています。

1 本目はゆっくり出て途中で伸び、縮みながら抜けていく曲線を使い、2 本目は素早く飛び出して減速する曲線を使います。2 本目は 1 本目より 1.15 秒遅れて追いかけるので、常にどちらか 1 本が流れている状態になります。

2 本を同じ曲線・同じタイミングにすると、帯が重なって 1 本に見えてしまうので、曲線と遅れを変えて重ならないようにします。動きを減らす設定では帯を流す代わりに、溝の明るさだけをゆっくり明滅させて、読み込み中だと伝えています。

帯の色は塗りのままでもグラデーションに替えても、位置がずれずに流れます。ページの切り替えやデータの取得中など、短い待ち時間をさりげなく知らせたい場面に向いています。

コード

HTML
<div class="bar-loader" role="status">
  <div class="bar-loader__track">
    <span class="bar-loader__fill bar-loader__fill--long"></span>
    <span class="bar-loader__fill bar-loader__fill--short"></span>
  </div>
  <span class="bar-loader__text">読み込み中</span>
</div>

CSS
.bar-loader {
  --fill: #98B1C4;    /* 帯の色(スレート) */
  --track: rgba(238, 236, 230, 0.14); /* 溝の色 */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
}

.bar-loader__track {
  position: relative;
  height: 4px;
  overflow: hidden;
  background: var(--track);
}

.bar-loader__fill {
  position: absolute;
  top: 0;
  bottom: 0;
  background: var(--fill);
}

.bar-loader__text {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

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

gsap.registerPlugin(CustomEase);

const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

function createLoader(root) {
  const track = root.querySelector(".bar-loader__track");
  const [longBar, shortBar] = root.querySelectorAll(".bar-loader__fill");
  let a, b;

  if (reduceMotion) {
    // 帯を流す代わりに、溝の明るさだけをゆっくり明滅させる
    gsap.set([longBar, shortBar], { autoAlpha: 0 });
    a = gsap.to(track, { opacity: 0.45, duration: 1, ease: "sine.inOut", yoyo: true, repeat: -1 });
  } else {
    // 1 本目はゆっくり出て途中で伸び縮みながら抜ける曲線、2 本目は素早く飛び出して減速する曲線
    const long = CustomEase.create("barLong", "M0,0 C0.65,0.815 0.735,0.395 1,1");
    const short = CustomEase.create("barShort", "M0,0 C0.165,0.84 0.44,1 1,1");
    a = gsap.fromTo(longBar, { left: "-35%", right: "100%" }, { left: "100%", right: "-90%", duration: 1.26, ease: long, repeat: -1, repeatDelay: 0.84 });
    b = gsap.fromTo(shortBar, { left: "-200%", right: "100%" }, { left: "107%", right: "-8%", duration: 1.26, ease: short, repeat: -1, repeatDelay: 0.84, delay: 1.15 });
  }

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

export const loader = createLoader(document.querySelector(".bar-loader"));

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

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