減速ルーレットで学ぶ時間ベースアニメーション設計

JavaScriptで減速するルーレットのアイキャッチ画像

JavaScriptで「自然に止まる」アニメーションを作る

これまで学んできたこと:

今回はそれらをすべて使って、

減速しながら止まるルーレットを作ります。

ここからが「設計」です。

1. まず完成イメージ

やりたいことはこれです。

  1. クリックすると回転開始
  2. 最初は速い
  3. だんだん減速
  4. ぴたっと止まる

単純そうに見えて、
実は時間設計が重要です。

2. よくある失敗パターン

初心者がやりがちなのはこれです。

speed *= 0.98;

毎フレーム少しずつ速度を落とす方法。

一見動きますが、問題があります:

  • PC性能で止まる時間が変わる
  • 60fps前提になっている
  • 止まるタイミングが曖昧

つまり

フレーム依存の減速は不安定

ここで #02 の deltaTime が活きます。

3. 正しい考え方:「時間で制御する」

大事なのは

「何秒で止めるか」を決める

フレームではなく、経過時間で管理します。

4. 基本設計

必要な変数

let startTime = null;
const duration = 3000; // 3秒で止める

アニメーション関数

function animate(timestamp) {
  if (!startTime) startTime = timestamp;

  const elapsed = timestamp - startTime;
  const progress = Math.min(elapsed / duration, 1);

  const eased = 1 - Math.pow(1 - progress, 3); // easeOutCubic

  const angle = 360 * 5 * (1 - eased);
  roulette.style.transform = `rotate(${angle}deg)`;

  if (progress < 1) {
    requestAnimationFrame(animate);
  }
}

5. 何をしているの?

progress

elapsed / duration

0 → 1 に進む値。

easing

1 - (1 - progress)^3

最初速くて、最後ゆっくり。

なぜ (1 – eased) なの?

ルーレットは

  • 最初たくさん回る
  • 最後は止まる

なので「減っていく値」にしています。

6. 設計の本質

このルーレットで一番重要なのは

「速度」を直接いじらないこと

速度を毎フレーム減らすのではなく、

  • 全体の時間
  • 進行率
  • easing

で動きを決める。

これが

状態設計型アニメーション

です。

7. 停止処理は必要?

今回は

progress < 1

でループ終了しています。

ですが、
ボタンで途中停止させる場合は

cancelAnimationFrame(id);

が必要になります。

#05 cancelAnimationFrame設計 と繋がります。

8.実験:減速の仕組みを観察する

🎯 目的

  • progress の増え方を見る
  • easing の変化を見る
  • フレーム依存でないことを確認する

👀 観察ポイント

  1. 何回クリックしても毎回3秒で止まる
  2. PCが重くても停止時間は変わらない
  3. 最初速く、最後ゆっくり止まる

🔗 次に繋がる話

ここまでで

  • 時間制御
  • easing
  • 停止設計

が揃いました。

次はシリーズ最終回:

#07 アニメーション設計まとめ

「結局どう組み立てるのか?」を整理します。

🔎 このラボの全体像はこちら
UI Architecture Roadmap

コメント

コメントを残す

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

CAPTCHA