→ easingやスクロールに関係する基礎はこちら
#03 easing / cubic-bezier
#18 Progress Bar Animation
ページを読んでいるとき、上部に細いバーが伸びていくUIを見たことはないでしょうか。
あれが Scroll Progress です。
スクロール位置に応じて進捗が可視化されるので、ユーザーは「今どのくらい読んだか」を直感的に把握できます。特に、記事ページやドキュメントページのように縦長なコンテンツと相性が良いUIです。
今回は、JavaScriptでScroll Progressバーを作る基本実装を、できるだけシンプルに整理して紹介します。
HTML・CSS・JavaScriptをそのままコピペすれば動く形でまとめているので、まずは一度動かしてみてください。
1.Scroll Progressとは?
Scroll Progressは、ページ全体のうち、現在どこまでスクロールしたかをバーで示すUIです。
たとえば次のような場面で使いやすいです。
- 記事ページの読了率を見せたいとき
- 長い解説ページで現在位置をわかりやすくしたいとき
- UIに少しだけ動きを加えて、読みやすさを上げたいとき
目立ちすぎないのに、あると便利なUIなので、比較的導入しやすいのもポイントです。
2.まずは完成形
今回作るのは、画面上部に固定された細いバーが、スクロールに応じて横に伸びるシンプルなパターンです。
- ページの最上部では 0%
- 一番下まで読むと 100%
- スクロール位置に合わせてバー幅を更新
この仕組みだけでも、かなりそれっぽく見えます。
3.実装の考え方
Scroll Progressの考え方はとてもシンプルです。
必要なのは次の3つです。
- 現在どれだけスクロールしたかを取得する
- スクロール可能な全体量を求める
現在位置 ÷ 全体量で進捗率を出す
式で書くとこうです。
progress = scrollTop / (documentHeight - windowHeight)この値を 0〜1 の範囲で扱い、バーの幅に変換すれば完成です。
→ スムーズな値の変化はここで解説しています
#11 Interpolation
4.完全コピペで動くサンプル
以下のコードをそのまま保存すれば動きます。
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Scroll Progress Demo</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="progress-wrap">
<div class="progress-bar" id="progressBar"></div>
</div>
<main class="container">
<h1>Scroll Progress Demo</h1>
<p>
このページは、スクロール位置に応じて上部のプログレスバーが伸びるサンプルです。
下へスクロールして挙動を確認してみてください。
</p>
<section>
<h2>Section 1</h2>
<p>Scroll Progressは、長い記事ページと相性が良いUIです。</p>
<p>読者が「あとどれくらいで読み終わるか」を把握しやすくなります。</p>
<p>ページ上部の細いバーだけでも、体験が少し整って見えます。</p>
</section>
<section>
<h2>Section 2</h2>
<p>実装の基本は、現在のスクロール位置を全体の高さで割ることです。</p>
<p>JavaScriptでは scrollTop や scrollHeight、clientHeight を使います。</p>
<p>比率が出せれば、width に反映するだけで見た目が完成します。</p>
</section>
<section>
<h2>Section 3</h2>
<p>記事、ドキュメント、利用規約、チュートリアルなどにも応用できます。</p>
<p>色や高さを変えれば、ブランドに合わせた見せ方も可能です。</p>
<p>派手な演出を入れなくても、使いやすさに直結しやすいUIです。</p>
</section>
<section>
<h2>Section 4</h2>
<p>今回は最小構成ですが、応用として数値表示や目次連動もできます。</p>
<p>まずはシンプルに、確実に動く形を押さえるのがおすすめです。</p>
<p>最後までスクロールして、バーが100%になることを確認してみてください。</p>
</section>
<section>
<h2>Section 5</h2>
<p>ダミーテキストです。スクロール量を増やすために少し長めにしています。</p>
<p>ダミーテキストです。スクロール量を増やすために少し長めにしています。</p>
<p>ダミーテキストです。スクロール量を増やすために少し長めにしています。</p>
<p>ダミーテキストです。スクロール量を増やすために少し長めにしています。</p>
<p>ダミーテキストです。スクロール量を増やすために少し長めにしています。</p>
<p>ダミーテキストです。スクロール量を増やすために少し長めにしています。</p>
</section>
</main>
<script src="script.js"></script>
</body>
</html>CSS
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
font-family: sans-serif;
line-height: 1.8;
color: #1f2937;
background: #f8fafc;
}
.progress-wrap {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: rgba(0, 0, 0, 0.06);
z-index: 9999;
}
.progress-bar {
width: 0%;
height: 100%;
background: linear-gradient(90deg, #34d399, #10b981);
transform-origin: left center;
transition: width 0.08s linear;
}
.container {
width: min(720px, calc(100% - 32px));
margin: 0 auto;
padding: 48px 0 120px;
}
h1 {
margin-bottom: 16px;
font-size: 32px;
line-height: 1.3;
}
h2 {
margin-top: 56px;
margin-bottom: 16px;
font-size: 24px;
}
p {
margin: 0 0 16px;
}JavaScript
const progressBar = document.getElementById("progressBar");
function updateScrollProgress() {
const scrollTop = window.scrollY || document.documentElement.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = document.documentElement.clientHeight;
const scrollable = scrollHeight - clientHeight;
if (scrollable <= 0) {
progressBar.style.width = "0%";
return;
}
const progress = (scrollTop / scrollable) * 100;
progressBar.style.width = `${progress}%`;
}
window.addEventListener("scroll", updateScrollProgress);
window.addEventListener("resize", updateScrollProgress);
window.addEventListener("load", updateScrollProgress);コードのポイント
ここでは、JavaScriptの処理をざっくり確認しておきます。
1. 現在のスクロール位置を取得する
const scrollTop = window.scrollY || document.documentElement.scrollTop;これは、今どれだけ下にスクロールしたかを表します。
2. ページ全体の高さと画面の高さを使う
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = document.documentElement.clientHeight;
const scrollable = scrollHeight - clientHeight;scrollHeight はページ全体の高さ、clientHeight は表示領域の高さです。
この差分が、実際にスクロールできる量になります。
3. 進捗率をバー幅に反映する
const progress = (scrollTop / scrollable) * 100;
progressBar.style.width = `${progress}%`;進捗率をそのまま % にして、バーの横幅に使っています。
これだけで、スクロール量に応じてバーが伸びます。
5.Scrollイベントで更新する理由
今回のサンプルでは、scroll イベントが発生するたびに進捗を更新しています。
window.addEventListener("scroll", updateScrollProgress);Scroll Progressは、ユーザーのスクロール操作に追従するUIなので、まずはこの方法がもっともわかりやすいです。
ただし、もっと凝った実装にしたい場合は、次のような工夫もできます。
requestAnimationFrameで更新タイミングを整えるtransform: scaleX()を使って描画負荷を抑える- 特定のコンテンツ領域だけを対象にする
まずは基本形を理解してから、必要に応じて発展させるのがよいと思います。
→ より安定した更新処理はこちら
#08 ループ設計
#09 Fixed Time Step
6.もう少し実用寄りにするなら
実案件やブログで使うなら、次のような調整もよくあります。
バーを細くして目立ちすぎないようにする
.progress-wrap {
height: 3px;
}記事の邪魔をしないUIにしたいなら、かなり有効です。
幅ではなく transform を使う
.progress-bar {
width: 100%;
transform: scaleX(0);
}progressBar.style.transform = `scaleX(${progress / 100})`;この形にすると、場合によっては描画がより滑らかに見えることがあります。
とくにアニメーション系UIでは、width より transform を使う設計が好まれることもあります。
記事本文だけを対象にする
ページ全体ではなく、本文エリアの読み進み具合だけを測りたいケースもあります。
その場合は article 要素などの高さを基準に計算すると、より実用的です。
7.よくあるつまずき
バーが最後まで100%にならない
コンテンツの高さ取得や余白の影響で、計算がずれることがあります。
まずは今回のように document.documentElement.scrollHeight と clientHeight を使う基本形で確認するのがおすすめです。
ページが短すぎて変化しない
スクロール量がほとんどないページでは、進捗バーもほぼ動きません。
動作確認時は、ダミーの文章を増やして縦長ページにするとわかりやすいです。
スクロール時に少しカクつく
今回のサンプルはシンプルさ優先なので問題ありませんが、
より丁寧に作るなら次のような改善余地があります。
requestAnimationFrameを使うtransformベースに切り替える- update処理を必要最小限にする
8.実験:Scroll Progressバーの伸び方を観察する
上部のバーが、スクロール位置に応じてどのように変化するかを確認してみましょう。
シンプルなUIですが、ページ全体の読了感がかなり変わることがわかります。
観察ポイント
- スクロール開始直後からバーが少しずつ伸びるか
- ページ終端でほぼ100%まで到達するか
- バーの高さや色を変えると印象がどう変わるか
width更新とtransform更新で体感差があるか
→ 他のUIモーションも見る
#20 Hover Animation
#23 Stagger Animation
#24 Scroll Reveal Animation
9.まとめ
Scroll Progressは、少ないコードで導入できて、体験を少し良くできるUIです。
今回のポイントは次の3つです。
- スクロール位置を取得する
- スクロール可能量で割って進捗率を出す
- その値をバー幅に反映する
たったこれだけですが、長いページではかなり有効です。
まずは今回の基本形をそのまま動かして、必要に応じて色・高さ・対象範囲を調整してみてください。

コメントを残す