STEP 01
企画
何を伝えるための動きなのかを先に決めます。目的のない演出は、読む人の邪魔になるだけです。
DEMO
STEP 01
何を伝えるための動きなのかを先に決めます。目的のない演出は、読む人の邪魔になるだけです。
STEP 02
動きの前後の状態と、かける時間を決めます。操作への反応は短めに収めると軽快に感じられます。
STEP 03
transform と opacity を中心に組み立てると、レイアウトを動かさずに軽く動かせます。
STEP 04
キーボードでの操作、動きを減らす設定、狭い画面での表示を確かめます。
STEP 05
公開後も、動きが目的に役立っているかを見直し続けます。
CODE
<div class="demo-stage trb-stage">
<div class="trb-scroller" id="trb-scroller" tabindex="0" role="region" aria-label="タイムラインのデモ(スクロールできます)">
<div class="trb-inner">
<div class="trb-rail" aria-hidden="true"><span class="trb-beam" id="trb-beam"></span><span class="trb-head" id="trb-head"></span></div>
<article class="trb-item"><p class="trb-step">STEP 01</p><h3>企画</h3><p>何を伝えるための動きなのかを先に決めます。目的のない演出は、読む人の邪魔になるだけです。</p></article>
<article class="trb-item"><p class="trb-step">STEP 02</p><h3>デザイン</h3><p>動きの前後の状態と、かける時間を決めます。操作への反応は短めに収めると軽快に感じられます。</p></article>
<article class="trb-item"><p class="trb-step">STEP 03</p><h3>実装</h3><p>transform と opacity を中心に組み立てると、レイアウトを動かさずに軽く動かせます。</p></article>
<article class="trb-item"><p class="trb-step">STEP 04</p><h3>検証</h3><p>キーボードでの操作、動きを減らす設定、狭い画面での表示を確かめます。</p></article>
<article class="trb-item"><p class="trb-step">STEP 05</p><h3>公開</h3><p>公開後も、動きが目的に役立っているかを見直し続けます。</p></article>
</div>
</div>
</div>
.trb-stage { display:flex; align-items:center; justify-content:center; padding:1.2rem; background:#0B0B10; min-height:320px; }
.trb-scroller { width:100%; max-width:400px; height:300px; overflow-y:auto; border-radius:14px; background:#111117; border:1px solid rgba(255,255,255,.08); outline:none; }
.trb-scroller:focus-visible { box-shadow:0 0 0 3px #E91B89; }
.trb-inner { position:relative; padding:1.2rem 1.2rem 1.2rem 3rem; }
.trb-rail { position:absolute; left:1.4rem; top:1.4rem; bottom:1.4rem; width:2px; background:rgba(255,255,255,.1); }
.trb-beam { position:absolute; left:0; top:0; width:100%; height:100%; transform-origin:top; transform:scaleY(0); background:linear-gradient(#5BAADE, #7B37A8 60%, #E91B89); transition: transform .1s linear; }
.trb-head { position:absolute; left:50%; top:0; width:12px; height:12px; margin:-6px 0 0 -6px; border-radius:50%; background:#fff; box-shadow:0 0 0 4px rgba(233,27,137,.35), 0 0 16px #E91B89; transition: transform .1s linear; }
.trb-item { padding-bottom:1.6rem; color:#C9C9D1; font-size:.82rem; line-height:1.7; }
.trb-item h3 { margin:.1rem 0 .3rem; color:#fff; font-size:1rem; }
.trb-item p { margin:0; }
.trb-item .trb-step { font-family:'JetBrains Mono',monospace; font-size:.7rem; letter-spacing:.08em; color:#F472B6; }
@media (prefers-reduced-motion: reduce) {
.trb-beam, .trb-head { transition-duration:.01ms; }
}
(function(){
var sc = document.getElementById('trb-scroller');
var beam = document.getElementById('trb-beam');
var head = document.getElementById('trb-head');
if (!sc || !beam || !head) return;
var rail = beam.parentNode, ticking = false;
function update(){
ticking = false;
var max = sc.scrollHeight - sc.clientHeight;
var p = max > 0 ? sc.scrollTop / max : 1;
beam.style.transform = 'scaleY(' + p + ')';
head.style.transform = 'translateY(' + (p * rail.clientHeight) + 'px)';
}
sc.addEventListener('scroll', function(){ if (!ticking) { ticking = true; requestAnimationFrame(update); } }, { passive: true });
window.addEventListener('resize', update);
update();
})();
AI PROMPT
縦に並んだ記事の左側で、スクロールした分だけ光の線が伸び、先端の点が読んでいる位置を示すタイムライン(Tracing Beam)を作って。高さ300px・overflow-y:auto の枠に記事を5つ並べ、左に余白を取って細い縦線(レール)を position:absolute で置く。レールの中に、上から下へのグラデーションの線を transform-origin: top・transform: scaleY(0〜1) で伸ばす(height を動かさない)。割合は 枠の scrollTop ÷ (scrollHeight − clientHeight) で求め、スクロールしきれない時は1にする。先端には白い丸に光の box-shadow を付けた点を置き、transform: translateY(割合 × レールの高さ) で線の先に合わせる。scroll イベントは passive: true で受け、requestAnimationFrame で1フレームに1回だけ更新する。枠は tabindex=0 と role="region"・aria-label を付けてキーボードでもスクロールできるようにし、overscroll-behavior は指定しない(枠の端まで来たらページ全体のスクロールへ引き継ぐ)。線と点は aria-hidden="true"。@media (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アソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
記事や手順の一覧の横で、スクロールした分だけ光の線が伸び、先端の点が今読んでいる位置を示す演出です。Aceternity UI にも同じ系統のコンポーネント(Tracing Beam)があり、公式の説明では SVG の線に沿って、スクロールに合わせて光が進むとされています。本ページは SVG を使わず、div の線を scaleY で伸ばす作りにしています。
当サイトの Scroll Progress Bar はページの上端に横棒で進み具合を示す表示、Scroll-driven Progress は CSS のスクロール連動アニメーションで作る進捗、Sticky Sections はスクロールに合わせて区画が貼り付く演出です。本ページは縦に並んだ内容の横に、読み進めた距離を線で示す別テーマです。
手順・沿革・更新履歴のように、上から順に読むことに意味がある内容と相性が良い演出です。線はあくまで補助なので、内容そのものは線がなくても順番が分かるよう、番号や見出しを付けておいてください。
ページ全体のスクロールに合わせるには?
枠の scrollTop の代わりに window.scrollY を、枠の高さの差の代わりに「document.documentElement.scrollHeight − window.innerHeight」を使えば、ページ全体の進み具合になります。CSS だけで作るなら animation-timeline: scroll() で線の伸びをスクロールに結びつける方法もありますが、MDN の互換性データでは Chrome と Edge は115、Safari は26から対応で、Firefox の正式版は未対応とされています。対応していないブラウザ向けに、本ページの JavaScript 版を残しておくと確実です。
線を height ではなく scaleY で伸ばすのはなぜですか?
height を変えると、スクロールのたびにレイアウトが計算し直されます。scaleY は描画の段階で伸び縮みさせるだけなので、スクロールに合わせて頻繁に更新しても軽く動きます。transform-origin を top にしておくと、上端を固定したまま下へ伸びていきます。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。