JavaScript | 1 日 120 分 × 7 日アプリ学習:タイマー & ストップウォッチ

JavaScript
スポンサーリンク

7日目のゴールと今日やること

7日目のテーマは
「タイマー & ストップウォッチ中級編の総仕上げとして、“設計を理解し、応用できる状態”になること」
です。

ここまでであなたは、

  • setInterval / setTimeout の役割
  • Date.now() を使った正確な時間管理
  • 開始 / 停止 / 一時停止 / 再開 / リセット
  • ミリ秒表示
  • 多重起動防止(状態管理)
  • 複数タイマーの同時管理

といった“時間アプリの本質”をすべて体験しました。

7日目はこれらをまとめて、

  • 時間アプリの「共通構造」を理解する
  • 状態管理を軸にした設計を言語化する
  • setTimeout と setInterval の使い分けを整理する
  • 自分で応用できるように「設計図」を作る

という、中級者としての視点を身につけます。


時間アプリの「共通構造」を理解する

時間アプリはすべて「3つの柱」でできている

ストップウォッチ
カウントダウン
複数タイマー
ポモドーロタイマー
アラーム

これらは見た目が違っても、
内部構造はほぼ同じです。

その「3つの柱」がこちら。

  1. 時間の計算ロジック
  2. 繰り返し処理(setInterval)
  3. 状態管理(running / paused / stopped)

この3つが揃えば、
どんな時間アプリでも作れます。

深掘り:時間の計算は「差分」で行う

時間アプリの本質は、

“今の時刻と基準時刻の差を取る”

という一点にあります。

ストップウォッチ
→ 経過時間 = (今の時刻 – startTime) + 過去の累積時間

カウントダウン
→ 残り時間 = endTime – 今の時刻

複数タイマー
→ タイマーごとに上記を計算するだけ

この「差分の考え方」を理解していると、
どんな時間アプリでも迷わなくなります。


setInterval と setTimeout の使い分けを整理する

setInterval の役割

setInterval は
“一定間隔で update を呼び続けるエンジン”
です。

ストップウォッチ → 0.01秒ごとに経過時間を更新
カウントダウン → 0.01秒ごとに残り時間を更新
複数タイマー → タイマーごとに update を呼ぶ

つまり、
時間アプリの本体ロジックは setInterval が担当
します。

setTimeout の役割

setTimeout は
“単発の遅延処理”
に向いています。

例えば、

  • 終了時の点滅演出
  • 3 → 2 → 1 → START のカウントダウン
  • ボタンの一時的な無効化
  • アニメーションの間隔調整

など、
「一回だけ遅らせたい」
という場面で使います。

深掘り:setTimeout の再帰は「回数付きの繰り返し」

setTimeout を再帰的に呼ぶと、

function blink(n) {
  if (n <= 0) return;
  element.classList.toggle("blink");
  setTimeout(() => blink(n - 1), 200);
}
JavaScript

このように
“回数 × 間隔”
の繰り返しが作れます。

setInterval では難しい制御なので、
中級者はこの使い分けを自然に行います。


状態管理が「多重起動防止」の核心

状態管理とは?

タイマーアプリは、
ユーザーの操作によって状態が変わります。

stopped
running
paused

この3つの状態を
文字列で管理する
のが最もシンプルで強力です。

状態による操作制御

例:start の場合

function start() {
  if (state === "running") return; // 多重起動防止
  if (state === "stopped") {
    // 初回スタートの処理
  }
  if (state === "paused") {
    // 再開の処理
  }
}
JavaScript

例:pause の場合

function pause() {
  if (state !== "running") return;
  // 停止処理
}
JavaScript

深掘り:状態管理は「アプリの安全装置」

状態管理がしっかりしていると、

  • start の連打で暴走しない
  • pause の連打でバグらない
  • reset の途中で変な動きにならない
  • 複数タイマーでも破綻しない

という“安定したアプリ”になります。


時間アプリの「設計図」を作る

設計図の構造

時間アプリは、
次の 5 つの関数を軸に設計すると美しくまとまります。

  1. start
  2. update
  3. pause
  4. resume
  5. reset

これに加えて、

  • 時間フォーマット関数(formatTime)
  • 表示更新関数(renderTime)
  • 状態管理(state)
  • タイマーID管理(timerId)

を組み合わせると、
どんな時間アプリでも作れます。

例:ストップウォッチの設計図(要点)

let startTime = 0;
let elapsedBefore = 0;
let timerId = null;
let state = "stopped";

function start() {
  if (state === "running") return;
  if (state === "stopped") elapsedBefore = 0;

  startTime = Date.now();
  timerId = setInterval(update, 10);
  state = "running";
}

function update() {
  const now = Date.now();
  const elapsed = elapsedBefore + (now - startTime);
  renderTime(elapsed);
}

function pause() {
  if (state !== "running") return;

  elapsedBefore += Date.now() - startTime;
  clearInterval(timerId);
  state = "paused";
}

function resume() {
  if (state !== "paused") return;

  startTime = Date.now();
  timerId = setInterval(update, 10);
  state = "running";
}

function reset() {
  clearInterval(timerId);
  elapsedBefore = 0;
  state = "stopped";
  renderTime(0);
}
JavaScript

深掘り:この設計図は「どんな時間アプリにも応用できる」

カウントダウンなら
elapsed → remaining に変わるだけ。

複数タイマーなら
この設計図を「タイマーごとに持つ」だけ。

ポモドーロなら
25分 → 5分 → 25分 → 5分
と「複数のカウントダウンを連結」するだけ。


setTimeout を使った「終了演出」を仕上げる

例:終了時に 3 回点滅

function finishEffect() {
  let count = 3;

  function blink() {
    timeDisplay.classList.toggle("blink");
    count--;
    if (count > 0) {
      setTimeout(blink, 200);
    }
  }

  blink();
}
JavaScript

CSS

.blink {
  opacity: 0.2;
}

深掘り:演出は「ロジックと分離」する

時間計算ロジックと
UI 演出は完全に分けることで、

  • バグが減る
  • コードが読みやすくなる
  • 演出を自由に変えられる

というメリットがあります。


今日いちばん深く理解してほしいこと

7日目の本質は、

「時間アプリはすべて同じ構造で作れる」
という理解です。

その構造とは、

  • 時間の差分計算
  • setInterval による繰り返し
  • 状態管理による制御
  • UI は状態の写し
  • setTimeout は演出用

という、
中級者としての“設計の視点”です。

ここまで理解できたあなたは、
もう「時間アプリを作れる人」ではなく、
“時間アプリを設計できる人”です。


次のステップとしては、

  • ポモドーロタイマー
  • マルチアラーム
  • ストップウォッチ+ラップ記録の保存
  • タイマーのプリセット管理
  • UI のアニメーション強化

など、
あなたのアイデア次第でいくらでも発展できます。

ここまで本当に素晴らしい成長でした。

タイトルとURLをコピーしました