Phase 1
Animation Fundamentals
JavaScriptで「自然に止まる」アニメーションを作る
これまで学んできたこと:
- #01
requestAnimationFrame= 毎フレーム処理する仕組み - #02
deltaTime= フレームごとの時間差 - #03
easing= なめらかな減速 - #04 フレームレートと負荷
- #05
cancelAnimationFrame= ループの停止設計
今回はそれらをすべて使って、
減速しながら止まるルーレットを作ります。
ここからが「設計」です。
1. まず完成イメージ
やりたいことはこれです。
- クリックすると回転開始
- 最初は速い
- だんだん減速
- ぴたっと止まる
単純そうに見えて、
実は時間設計が重要です。
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 / duration0 → 1 に進む値。
easing
1 - (1 - progress)^3最初速くて、最後ゆっくり。
なぜ (1 – eased) なの?
ルーレットは
- 最初たくさん回る
- 最後は止まる
なので「減っていく値」にしています。
6. 設計の本質
このルーレットで一番重要なのは
「速度」を直接いじらないこと
速度を毎フレーム減らすのではなく、
- 全体の時間
- 進行率
- easing
で動きを決める。
これが
状態設計型アニメーション
です。
7. 停止処理は必要?
今回は
progress < 1でループ終了しています。
ですが、
ボタンで途中停止させる場合は
cancelAnimationFrame(id);が必要になります。
#05 cancelAnimationFrame設計 と繋がります。
8.実験:減速の仕組みを観察する
🎯 目的
- progress の増え方を見る
- easing の変化を見る
- フレーム依存でないことを確認する
👀 観察ポイント
- 何回クリックしても毎回3秒で止まる
- PCが重くても停止時間は変わらない
- 最初速く、最後ゆっくり止まる
🔗 次に繋がる話
ここまでで
- 時間制御
- easing
- 停止設計
が揃いました。
次はシリーズ最終回:
「結局どう組み立てるのか?」を整理します。
🔎 このラボの全体像はこちら
→ UI Architecture Roadmap

コメントを残す