2日目のゴールと今日やること
2日目のテーマは
「ストップウォッチを“実用レベル”に進化させる」
ことです。
1日目であなたは、
- setInterval の基本
- Date.now() を使った経過時間の計算
- 多重起動防止
- ミリ秒表示
といった“タイマーの心臓部”を理解しました。
2日目はここに、
- 一時停止と再開
- ラップ(区切り)機能
- setTimeout を使った「遅延処理」
- 時間管理ロジックの強化(再開時のズレ補正)
を加えて、
「本物のストップウォッチに近いアプリ」を作っていきます。
一時停止と再開を正しく実装する
ストップウォッチの本質は「経過時間の累積」
ストップウォッチは
“スタートしてからの合計経過時間”
を表示するアプリです。
一時停止を入れると、
この「合計経過時間」をどう扱うかが重要になります。
一時停止で必要な情報
一時停止を実装するには、
次の 2 つを管理する必要があります。
- startTime(再開した瞬間の時刻)
- elapsedBefore(過去に積み上げた経過時間)
停止 → 再開の流れはこうなります。
停止時:
elapsedBefore に「今までの経過時間」を保存する
setInterval を止める
再開時:
startTime を「再開した瞬間の時刻」に更新
elapsedBefore を足しながら update で表示する
コード例
let startTime = 0;
let elapsedBefore = 0;
let timerId = null;
let isRunning = false;
function start() {
if (isRunning) return;
isRunning = true;
startTime = Date.now();
timerId = setInterval(update, 10);
}
function update() {
const now = Date.now();
const elapsed = elapsedBefore + (now - startTime);
display(elapsed);
}
function stop() {
if (!isRunning) return;
isRunning = false;
clearInterval(timerId);
elapsedBefore += Date.now() - startTime;
}
function reset() {
clearInterval(timerId);
isRunning = false;
startTime = 0;
elapsedBefore = 0;
display(0);
}
JavaScript深掘り:なぜ elapsedBefore が必要なのか?
もし elapsedBefore を使わずに
「停止 → 再開」したらどうなるでしょう?
再開した瞬間からの時間しか計算されず、
過去の経過時間が消えてしまいます。
ストップウォッチは
「累積時間」を扱うアプリなので、
過去の時間を保持する仕組みが必須なのです。
setTimeout を使った「遅延処理」を理解する
setTimeout は「一度だけ遅らせる」
ストップウォッチでは setInterval が主役ですが、
setTimeout もよく使います。
例えば、
- 「3秒後に自動スタート」
- 「停止して 1 秒後にメッセージを出す」
- 「一定時間だけボタンを無効化する」
などの“単発の遅延処理”に向いています。
例:停止後 1 秒だけボタンを無効化する
stopButton.disabled = true;
setTimeout(() => {
stopButton.disabled = false;
}, 1000);
JavaScript深掘り:setInterval と setTimeout の役割分担
setInterval → 繰り返し処理(ストップウォッチの更新)
setTimeout → 単発の遅延処理(UI の演出など)
この使い分けができると、
アプリの動きが一気に滑らかになります。
ラップ(区切り)機能を実装する
ラップとは?
ストップウォッチの「ラップ」は、
“今の経過時間を記録して一覧に追加する”
という機能です。
ラップを押すたびに、
その瞬間の時間を保存していきます。
ラップの実装
const laps = [];
function lap() {
const now = Date.now();
const elapsed = elapsedBefore + (now - startTime);
laps.push(elapsed);
renderLaps();
}
function renderLaps() {
const area = document.getElementById("laps");
area.innerHTML = "";
laps.forEach((ms, index) => {
const p = document.createElement("p");
p.textContent = `${index + 1}周目:${format(ms)}`;
area.appendChild(p);
});
}
JavaScript深掘り:ラップは「現在の経過時間をコピーするだけ」
ラップは難しそうに見えますが、
実は elapsed を配列に push するだけ です。
ストップウォッチのロジックを理解していれば、
自然に実装できます。
ミリ秒表示をより正確にする
ミリ秒は「ズレやすい」
setInterval(update, 10)
と書いても、
実際には 10ms ぴったりで動くとは限りません。
JavaScript のタイマーは
ブラウザの負荷によって遅れたり早まったりします。
Date.now() を使う理由
update の中で
「前回から 10ms 経った」と仮定するのではなく、
“実際に何ミリ秒経ったか”を Date.now() で計算する
ことで、ズレを補正できます。
これが
「時間管理ロジックの本質」
です。
多重起動防止をさらに強化する
isRunning だけでは不十分なケース
1日目で isRunning を導入しましたが、
中級編ではもう一段階強化します。
例えば、
- 停止 → すぐに再開 → 停止 → 再開
- リセット → 連打
- ラップ → 停止 → 再開 → ラップ
など、複雑な操作をすると
「想定外の状態」になりやすいです。
状態を明確に分ける
let state = "stopped";
// "stopped" | "running" | "paused"
JavaScriptstart → running
stop → paused
reset → stopped
というように、
状態を文字列で管理すると
アプリの安定性が一気に上がります。
例:start の条件分岐
function start() {
if (state === "running") return;
if (state === "stopped") {
elapsedBefore = 0;
}
startTime = Date.now();
timerId = setInterval(update, 10);
state = "running";
}
JavaScript深掘り:状態管理は「中級アプリの必須スキル」
状態を文字列で管理することで、
- どの操作が許されるか
- どの操作は無効か
を明確にできます。
これは
ゲーム開発、UI 制御、アニメーション制御など
あらゆるアプリで使われる考え方
です。
2日目の完成コード(重要部分のみ)
let startTime = 0;
let elapsedBefore = 0;
let timerId = null;
let state = "stopped"; // "stopped" | "running" | "paused"
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);
display(elapsed);
}
function stop() {
if (state !== "running") return;
clearInterval(timerId);
elapsedBefore += Date.now() - startTime;
state = "paused";
}
function reset() {
clearInterval(timerId);
startTime = 0;
elapsedBefore = 0;
state = "stopped";
display(0);
}
function lap() {
if (state !== "running") return;
const now = Date.now();
const elapsed = elapsedBefore + (now - startTime);
laps.push(elapsed);
renderLaps();
}
JavaScript今日いちばん深く理解してほしいこと
ストップウォッチは「累積時間 × 状態管理」で動いている
今日の内容をまとめると、
- 経過時間は「startTime と elapsedBefore」で管理する
- setInterval はズレるので Date.now() で補正する
- 一時停止は「累積時間を保存する」動き
- ラップは「今の経過時間をコピーするだけ」
- 多重起動防止は「状態管理」が最強
ということです。
これらを理解できたあなたは、
もう“タイマーアプリの中級者”です。
3日目へのつなぎ
3日目は、
- カウントダウンタイマー
- アラーム機能
- setTimeout と setInterval の併用
- 時間フォーマットの応用
など、
「タイマーアプリのもう一つの側面」
に進んでいきます。
ストップウォッチとカウントダウンは
ロジックが似ているようで全然違うので、
ここからさらに理解が深まります。

