スクロールと時間を組み合わせるUI設計【Scroll × Time UI】

Scroll Time UIのアイキャッチ画像

スクロール連動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シリーズ

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA