デモ
デモを読み込めませんでした。
仕様
- 用途
- ローディング
- 動作
- DOM
- 種類
- レシピ
- 動作確認
- three 0.186.0・gsap 3.15.0
- 公開日
しくみと調整
「LOADING」の文字の中だけを、明るい光の筋が左から右へ通り抜けていきます。文字の色は透明にして、中央に光の筋が入ったグラデーションを、background-clip で文字の形に切り抜いています。グラデーションの幅は文字の250%あり、光の筋は文字の外から入ってきて外へ抜けていきます。光が文字にかかっていないあいだに次の周へ切り替わるので、繰り返しの継ぎ目が見えません。
コードでは背景の位置(background-position)を100%から0%へ減らしていますが、見た目の光は左から右へ進みます。値の向きと見た目の向きが逆に見えるので、向きを変えたいときは読み違えないようにしてください。
光が端から端まで走るのは1.3秒、次の光までの間は0.3秒。動きは ease: none の等速にしました。イージングを付けると、端の近くで光がもたついて止まったように見え、流れている感じが途切れます。
背景の幅は、250%前後からあまり動かさないほうが安全です。広げるほど光が文字の外にいる時間が延びて、止まって見える瞬間が増えます。狭めすぎると光の外側の余白がなくなり、繰り返しの境目に継ぎ目が出ます。光の色や角度、走る速さを変えれば、送信中のボタンや生成中の表示など、短い文言に目を留めてほしい場所にも使えます。
コード
<!-- ローディングの文言。テキストそのものが読み上げにも届く -->
<div class="shimmer-loader">
<p class="shimmer-loader__text" role="status">LOADING</p>
</div>
.shimmer-loader {
--ground: #111111; /* 背景色。文字のグラデーションとは別の要素に付ける(同じ要素だと背景色まで文字の形に切り抜かれる) */
position: fixed;
inset: 0;
z-index: 10;
display: grid;
place-items: center;
padding: 0 6vw;
background: var(--ground);
}
.shimmer-loader__text {
--base: #898989; /* 光が当たっていない文字の色 */
--shine: #eeece6; /* 光の筋。当たったところだけ文字の色になる */
margin: 0;
color: transparent;
font-size: clamp(32px, 9vw, 80px);
font-weight: 800;
line-height: 1.15;
letter-spacing: 0.06em;
padding-left: 0.06em; /* 最後の字の後ろにも付く字間の分を左にも足し、字を中央にそろえる */
text-align: center;
background-image: linear-gradient(105deg, var(--base) 0%, var(--base) 44%, var(--shine) 50%, var(--base) 56%, var(--base) 100%);
background-size: 250% 100%;
background-repeat: no-repeat;
-webkit-background-clip: text;
background-clip: text;
}
import { gsap } from "gsap";
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const SWEEP = 1.3; // 光の筋が端から端まで走る時間(秒)
const REST = 0.3; // 次の光の筋までの間(秒)。光の筋は等速(ease: "none")にすると、文字にかかっている時間を長く取れる
function createLoader(label) {
const root = label.closest(".shimmer-loader");
const shimmer = reduceMotion
? gsap.set(label, { backgroundPosition: "50% 0%" }) // 動きを減らす設定では走らせず、光の筋を乗せたまま止める
: gsap.fromTo(
label,
{ backgroundPosition: "100% 0%" },
{ backgroundPosition: "0% 0%", duration: SWEEP, ease: "none", repeat: -1, repeatDelay: REST }
);
return {
// 読み込みが終わったら呼ぶ。光の筋を止めて、ローディング画面ごと消す
done() {
shimmer.kill();
gsap.to(root, { autoAlpha: 0, duration: reduceMotion ? 0 : 0.3, onComplete: () => root.remove() });
},
};
}
// 例: fetch や画像の読み込みを待って loader.done()。ボタンの「処理中」やAIの「生成中」表示にも
export const loader = createLoader(document.querySelector(".shimmer-loader__text"));