スクロール連動UIは、「スクロール量に応じて変化する」ものとして作ることが多いです。
たとえば、
- スクロールすると要素が移動する
- 進行度に応じてUIが変わる
といった表現です。
ただ、この方法だけだと、スクロールが止まった瞬間にすべての動きも止まるという特徴があります。
そこで今回は、スクロールに「時間」の概念を加えます。
- スクロール → 状態(state)
- 時間 → 変化の継続
この2つを組み合わせることで、
「操作に連動しつつ、少しだけ自律的に動くUI」 を作ることができます。
スクロールを「状態」として扱う基本については、前回の記事で詳しく解説しています。
👉 [スクロールを状態として扱うUI設計(Scroll-Driven State UI)]
1.ScrollだけのUIの限界
まずは、従来のスクロール連動UIを整理します。
const progress = scrollY / maxScroll;この progress を使えば、
- 位置
- サイズ
- 透明度
などをすべて制御できます。
ただし、この設計には1つ特徴があります。
👉 スクロールが止まると、すべて止まる
これはこれで正しい挙動ですが、場合によっては少し“機械的”に感じることもあります。
スクロール位置をきっかけに状態を切り替える実装については、
👉 [Scroll Trigger Animation] や
👉 [Scroll Spy UI] の記事でも扱っています。
今回のような「連続的な変化」とは、少しアプローチが異なります。
2.時間(Time)を追加する
ここで登場するのが「時間」です。
時間は、requestAnimationFrame を使って毎フレーム更新できます。
let time = 0;
function update(deltaTime) {
time += deltaTime;
}この time は、スクロールとは関係なく進み続ける値です。
つまり、
- Scroll → ユーザー入力
- Time → 自動的に進む値
という2つの軸ができることになります。
時間ベースでアニメーションを制御する方法については、
👉 [requestAnimationFrameの使い方] や
👉 [deltaTimeとは?] の記事で詳しく解説しています。
3.Scroll × Time の基本形
この2つを組み合わせると、次のような形になります。
const value = progress * 0.7 + time * 0.3;ここが今回の一番重要なポイントです。
👉 「スクロール」と「時間」を混ぜる
こうすることで、
- スクロールに連動しつつ
- 少しだけ動き続ける
という状態が作れます。
4.UIに適用してみる
たとえば、位置に使うとこうなります。
box.style.transform = `translateX(${value * 100}px)`;すると、
- スクロールすると大きく動く
- 止まっても少しだけ動き続ける
という挙動になります。
この「完全に止まらない感じ」が、UIに少し“生っぽさ”を与えます。
5.easingと組み合わせる
さらに、Scroll側に easing をかけると、動きがより自然になります。
const eased = progress * progress;
const value = eased * 0.7 + time * 0.3;- Scroll → ゆっくり始まる
- Time → 常に進む
👉 動きに「重さ」が出る
easingについて詳しく知りたい場合は、
👉 [easing / cubic-bezier] の記事もあわせて参考になります。
6.よくある失敗
Timeが強すぎる
value = progress * 0.2 + time * 0.8;👉 スクロールしても効いてる感じが薄くなる
Timeが弱すぎる
value = progress * 0.95 + time * 0.05;👉 ほぼScrollと変わらない
deltaTimeを使っていない
time += 1;👉 フレームレート依存になる
7.Scroll × Time UI の実装
今回は次の動きを作ります。
- スクロールで大きく動く
- 止まっても少し揺れる
- UI全体が連動する
HTML
<section class="stage">
<h1 id="title">Scroll × Time</h1>
<div class="box" id="box"></div>
<p id="label">0.00</p>
</section>
<section class="spacer"></section>CSS
body {
margin: 0;
height: 300vh;
background: #0f1115;
color: white;
font-family: sans-serif;
}
.stage {
position: sticky;
top: 0;
height: 100vh;
display: grid;
place-items: center;
}
.box {
width: 120px;
height: 120px;
background: linear-gradient(135deg, #7dd3fc, #a78bfa);
}
.spacer {
height: 200vh;
}JavaScript
const box = document.getElementById('box');
const label = document.getElementById('label');
let lastTime = performance.now();
let time = 0;
function clamp(v, min, max) {
return Math.min(Math.max(v, min), max);
}
function loop(now) {
const deltaTime = (now - lastTime) / 1000;
lastTime = now;
time += deltaTime;
const scrollTop = window.scrollY;
const maxScroll = document.body.scrollHeight - window.innerHeight;
const progress = clamp(scrollTop / maxScroll, 0, 1);
const eased = progress * progress;
const value = eased * 0.7 + Math.sin(time * 2) * 0.1;
box.style.transform = `translateX(${value * 300}px)`;
label.textContent = value.toFixed(2);
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);8.実験:止まらないUIを観察する
- スクロールすると大きく動く
- 止まっても、少し揺れ続ける
これは、
👉 Scroll(入力)
👉 Time(継続)
を混ぜているためです。
観察ポイント
- Scrollだけだと完全に止まる
- Timeを入れると動きが続く
- 比率を変えると印象が変わる
この考え方は、スクロールに応じてストーリー的にUIを切り替える
👉 [Scroll Story UI] や、
複数要素を同期させる設計にも応用できます。
9.まとめ
Scroll × Time UI のポイントは次の通りです。
- Scroll は状態(state)
- Time は変化の継続
- 2つを混ぜると表現が広がる
この考え方を使うと、
- スクロール連動UI
- アニメーション
- インタラクション
を自然につなげることができます。
Scroll Animationシリーズ
- #24 Scroll Reveal Animation
- #25 Scroll Progress
- #26 Scroll Snap Animation
- #27 Parallax Scroll Animation
- #28 Scroll Trigger Animation
- #29 Sticky Scroll Animation
- #30 Scroll Story UI
- #31 Scroll State Machine UI
- #32 Scroll Velocity Animation
- #33 Scroll Mouse Interaction UI
- #34 Scroll Spy UI
- #35 Scroll Sync UI
- #36 Scroll-Driven State UI
- #37 Scroll Time UI
- #38 Scroll Timeline UI
- #39 Scroll Direction UI

コメントを残す