デモ
デモを読み込めませんでした。
仕様
- 用途
- ローディング
- 動作
- DOM
- 種類
- レシピ
- 使うもの
- DrawSVGPlugin
- 動作確認
- three 0.186.0・gsap 3.15.0
- 公開日
しくみと調整
抽象的なロゴマークを、輪から 3 枚の葉の順に線で描いていきます。描き終えた部分は面の塗りに置き換わり、線が消えていくので「線から面へ」動いているように見えます。塗り終えると全体が軽く弾み、最後は縮んで消えます。
輪を描くのに 0.9 秒、3 枚の葉は 0.12 秒ずつずらしながら 0.8 秒で描きます。線から面への切り替えは 0.45 秒で、線がすべて消えると同時に塗りが完成します。始まりと終わりはどちらも何も見えていない状態なので、そのままループにつながります。
縮めて消す動きは、葉ごとの中心ではなく輪全体の中心に向けて縮めないと、それぞれの葉が別々の方向に消えていく不自然な見え方になります。動きを減らす設定では、線を描く動きをやめて塗った状態のまま明るさだけをゆっくり明滅させ、大きな動きを見せません。
色や形をロゴに合わせて差し替えれば、そのままブランドのローディング表示として使えます。読み込みが終わったら塗り終えた時刻で止めれば、マークが完成した姿でページに引き継げます。
コード
<div class="logo-loader" role="status">
<svg class="logo-loader__svg" viewBox="-100 -100 200 200" aria-hidden="true">
<g class="logo-loader__mark">
<circle class="logo-loader__ring" cx="0" cy="0" r="92" transform="rotate(-90)" />
<g transform="rotate(0)"><path class="logo-loader__leaf logo-loader__leaf--1" d="M0,-8 C-30,-26 -30,-58 0,-78 C30,-58 30,-26 0,-8 Z" /></g>
<g transform="rotate(120)"><path class="logo-loader__leaf logo-loader__leaf--2" d="M0,-8 C-30,-26 -30,-58 0,-78 C30,-58 30,-26 0,-8 Z" /></g>
<g transform="rotate(240)"><path class="logo-loader__leaf logo-loader__leaf--3" d="M0,-8 C-30,-26 -30,-58 0,-78 C30,-58 30,-26 0,-8 Z" /></g>
</g>
</svg>
<span class="logo-loader__text">読み込み中</span>
</div>
.logo-loader {
--size: 216px; /* マークの大きさ */
--ground: #111111; /* 地の色(Night) */
--line: #EEECE6; /* 線と 3 枚目の葉の色 */
--leaf1: #788b97; /* 1 枚目の葉(墨のスレート) */
--leaf2: #d2b999; /* 2 枚目の葉(グレージュ) */
position: fixed;
inset: 0;
z-index: 10;
display: grid;
place-items: center;
background: var(--ground);
}
.logo-loader__svg {
width: var(--size);
height: var(--size);
overflow: visible;
}
.logo-loader__ring {
fill: none;
stroke: var(--line);
stroke-width: 3;
}
.logo-loader__leaf {
fill-opacity: 0;
stroke: var(--line);
stroke-width: 3.5;
stroke-linejoin: round;
}
.logo-loader__leaf--1 { fill: var(--leaf1); }
.logo-loader__leaf--2 { fill: var(--leaf2); }
.logo-loader__leaf--3 { fill: var(--line); }
.logo-loader__text {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
import { gsap } from "gsap";
import { DrawSVGPlugin } from "gsap/DrawSVGPlugin";
gsap.registerPlugin(DrawSVGPlugin);
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
function createLoader(root) {
const ring = root.querySelector(".logo-loader__ring");
const leaves = root.querySelectorAll(".logo-loader__leaf");
const mark = root.querySelector(".logo-loader__mark");
const draw = gsap.timeline({ repeat: -1 });
if (reduceMotion) {
// 線を描く動きをやめ、塗った状態のまま明るさだけをゆっくり明滅させる
gsap.set(ring, { drawSVG: "100%" });
gsap.set(leaves, { drawSVG: "100%", fillOpacity: 1, strokeOpacity: 0 });
draw.to(mark, { opacity: 0.5, duration: 1.1, ease: "sine.inOut", yoyo: true, repeat: -1 });
} else {
// 輪 → 葉の順に線で描き、塗りを出しながら葉の線を消して「線 → 面」に移す
draw
.fromTo(ring, { drawSVG: "0%" }, { drawSVG: "100%", duration: 0.9, ease: "power2.inOut" }, 0.1)
.fromTo(leaves, { drawSVG: "0%" }, { drawSVG: "100%", duration: 0.8, ease: "power2.inOut", stagger: 0.12 }, 0.35)
.fromTo(leaves, { fillOpacity: 0, strokeOpacity: 1 }, { fillOpacity: 1, strokeOpacity: 0, duration: 0.45, ease: "power1.inOut", stagger: 0.1 }, 1.25)
.fromTo(mark, { scale: 1, svgOrigin: "0 0" }, { scale: 1.06, svgOrigin: "0 0", duration: 0.2, ease: "power2.out", yoyo: true, repeat: 1 }, 1.6)
.fromTo(ring, { drawSVG: "0% 100%" }, { drawSVG: "100% 100%", duration: 0.6, ease: "power2.in" }, 2.3)
.fromTo(leaves, { scale: 1, svgOrigin: "0 0" }, { scale: 0, svgOrigin: "0 0", duration: 0.5, ease: "back.in(1.7)", stagger: 0.08 }, 2.4);
}
return {
// 読み込みが終わったら呼ぶ
done() {
gsap.to(root, {
autoAlpha: 0,
duration: reduceMotion ? 0 : 0.3,
onComplete: () => {
draw.kill();
root.remove();
},
});
},
};
}
export const loader = createLoader(document.querySelector(".logo-loader"));