DEMO
記事一覧(1ページ目)
CODE
<div class="demo-stage tlb-stage">
<div class="tlb-window">
<div class="tlb-bar" id="tlb-bar" aria-hidden="true"><span class="tlb-peg"></span></div>
<div class="tlb-chrome" aria-hidden="true"><span></span><span></span><span></span><em>motion.example</em></div>
<div class="tlb-page">
<p class="tlb-title" id="tlb-title">記事一覧(1ページ目)</p>
<p class="tlb-status" id="tlb-status" role="status" aria-live="polite"></p>
<button type="button" class="tlb-btn" id="tlb-btn">次のページを読み込む</button>
</div>
</div>
</div>
.tlb-stage { display:flex; align-items:center; justify-content:center; padding:1.5rem; background:#0B0B10; min-height:280px; }
.tlb-window { position:relative; width:100%; max-width:380px; border-radius:12px; overflow:hidden; background:#15151B; border:1px solid rgba(255,255,255,.1); }
.tlb-bar { position:absolute; top:0; left:0; z-index:2; width:100%; height:3px; background:#E91B89; transform:translateX(-100%); opacity:0; transition: transform .3s ease, opacity .3s ease; }
.tlb-bar.is-on { opacity:1; }
.tlb-peg { position:absolute; top:0; right:0; width:80px; height:100%; box-shadow:0 0 10px #E91B89, 0 0 5px #E91B89; }
.tlb-chrome { display:flex; align-items:center; gap:6px; padding:.6rem .8rem; border-bottom:1px solid rgba(255,255,255,.08); }
.tlb-chrome span { width:9px; height:9px; border-radius:50%; background:rgba(255,255,255,.2); }
.tlb-chrome em { margin-left:.5rem; font-style:normal; font-size:.7rem; color:#A1A1AA; font-family:'JetBrains Mono',monospace; }
.tlb-page { display:flex; flex-direction:column; align-items:flex-start; gap:.6rem; padding:1.1rem 1rem 1.2rem; }
.tlb-title { margin:0; color:#fff; font-size:.95rem; font-weight:600; }
.tlb-status { margin:0; min-height:1.2em; color:#A1A1AA; font-size:.76rem; }
.tlb-btn { min-height:44px; padding:0 1.1rem; border:0; border-radius:10px; background:#D1177A; color:#fff; font-weight:600; font-size:.84rem; cursor:pointer; }
.tlb-btn:focus-visible { outline:2px solid #fff; outline-offset:3px; }
@media (prefers-reduced-motion: reduce) {
.tlb-bar { transition-duration:.01ms; }
}
(function(){
var bar = document.getElementById('tlb-bar');
var btn = document.getElementById('tlb-btn');
var title = document.getElementById('tlb-title');
var status = document.getElementById('tlb-status');
if (!bar || !btn || !title || !status) return;
var p = 0, trickle = 0, done = 0, hide = 0, page = 1;
function set(v){ p = v; bar.style.transform = 'translateX(' + ((p - 1) * 100) + '%)'; }
function clear(){ clearInterval(trickle); clearTimeout(done); clearTimeout(hide); trickle = done = hide = 0; }
function fadeOut(){
bar.classList.remove('is-on');
hide = setTimeout(function(){ bar.style.transition = 'none'; set(0); void bar.offsetWidth; bar.style.transition = ''; hide = 0; }, 300);
}
function start(){
clear();
bar.style.transition = 'none'; set(0); void bar.offsetWidth; bar.style.transition = '';
bar.classList.add('is-on');
set(0.08);
trickle = setInterval(function(){ set(p + (0.9 - p) * 0.12); }, 250);
done = setTimeout(finish, 2400);
status.textContent = '読み込み中…';
btn.textContent = '読み込みを中止';
}
function finish(){
clear();
set(1);
page++;
title.textContent = '記事一覧(' + page + 'ページ目)';
status.textContent = page + 'ページ目を読み込みました';
btn.textContent = '次のページを読み込む';
hide = setTimeout(fadeOut, 300);
}
function abort(){
clear();
fadeOut();
status.textContent = '読み込みを中止しました';
btn.textContent = '次のページを読み込む';
}
btn.addEventListener('click', function(){ if (trickle) abort(); else start(); });
})();
AI PROMPT
ページの読み込み中に、画面の上端を細いバーが少しずつ伸び、終わると右端まで伸びきってから消える読み込み表示を作って(NProgress のような見た目)。バーは高さ3px・幅100%で上端に置き、transform: translateX(-100%) から始めて、進み具合 p(0〜1)に合わせて translateX((p − 1) × 100%) で右へ出していく(width は動かさない)。右端には box-shadow で光る部分を付ける(scaleX で伸ばすと光がゆがむので translateX を使う)。読み込みを始めたら p を0.08にし、250ミリ秒ごとに p に (0.9 − p) × 0.12 を足して、90%に近づくほど遅く進ませる(終わりが分からない間、止まって見えないようにするため)。読み込みが終わったら p を1にして、0.3秒後に透明にし、さらに0.3秒後に transition を一時的に外して0へ戻す(戻る動きを見せない)。デモでは2.4秒後に完了した扱いにする(実際には読み込みの完了時に完了の関数を呼ぶ)。バーは aria-hidden="true" にして、状態は role="status" の段落で「読み込み中…」「2ページ目を読み込みました」と伝える。読み込み中はボタンを「読み込みを中止」に変え(押せない状態にはしない)、中止したらバーを消す。デモの枠の中に置くため、バーは position:absolute にする(ページ全体に使う時は画面の上端に固定する)。prefers-reduced-motion: reduce では transition を無効化する。 Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift
AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する
BOOKS · PR · AMAZON
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
CREATOR TOOLS · スクール(PR)
※本サイトはアフィリエイトリンクを含みます。リンク経由のご購入・お申し込みで運営者が紹介料を受け取る場合があります。
ページの読み込み中に、画面の上端を細いバーが伸びていく表示です。NProgress はこの形の代表的なライブラリで、README では「YouTube や Medium などにあるような細い進捗バーのため」のものと説明されています。画面全体を覆わないので、読み込みの間も今のページを見ていられます。
当サイトの Progress Bar は読み込みの進捗を視覚化する基本のバー、Indeterminate Progress は終わりが分からない間に帯がバーの中を流れ続ける不定の表示、Scroll Progress Bar はスクロール量に応じて上部のバーが伸びる読了表示です。本ページは画面の切り替えを待つ間の表示に特化し、90%に近づくほど遅くなる進め方と、終わった後の消え方までを扱います。
バーはあくまで「待っている」ことを示す目印で、実際の進み具合を正確に表すものではありません。完了の合図は実際の読み込みの完了に合わせ、読み上げを使う人には状態を文章でも伝えてください。
90%に近づくほど遅く進むのはなぜですか?
読み込みがいつ終わるかは分からないため、一定の速さで進めると、終わる前にバーが右端へ着いてしまいます。本実装は「90%までの残り」の12%ずつを足していくので、最初は速く、90%に近づくほど遅くなり、完了の知らせが来るまで右端には届きません。NProgress の README では、この自動で少しずつ進める動きを trickle と呼んでいます。
ページ全体の読み込みに使うにはどうしますか?
バーを position:fixed で画面の上端に置き、z-index を高くします。ページの移動を JavaScript で行うサイト(SPA)なら、移動を始めた時に開始の関数、新しい画面を表示し終えた時に完了の関数を呼びます。バーは細く、下にある要素のクリックを邪魔しないよう pointer-events:none を付け、状態は読み上げ向けに別の方法でも伝えてください。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。