5日目のゴールと今日やること
5日目のテーマは
「タイマー & ストップウォッチを“UI と操作性の面から強化する”」
ことです。
ここまでであなたは、
- setInterval / setTimeout の違い
- Date.now() を使った正確な時間管理
- 開始 / 停止 / 一時停止 / 再開 / リセット
- ミリ秒表示
- 多重起動防止(状態管理)
といった“時間アプリの基礎ロジック”を完全に理解しました。
5日目はここに、
- ボタンの活性・非活性制御
- UI の状態変化(色・点滅・アニメーション)
- setTimeout を使った「一時的な演出」
- 状態管理を UI に反映する
- 入力チェックの強化
という、“アプリとしての使いやすさ”を加えていきます。
ボタンの活性・非活性を状態に応じて切り替える
なぜ必要なのか
タイマー系アプリは、
「押してはいけないタイミング」が多いです。
例えば、
- running 中に start を押してはいけない
- stopped 中に pause を押しても意味がない
- paused 中に reset はできるが start はできない
これを UI で防ぐと、
ユーザーが迷わず操作できます。
状態に応じたボタン制御
状態をこう定義します。
let state = "stopped";
// "stopped" | "running" | "paused"
JavaScriptボタン制御関数を作ります。
function updateButtons() {
if (state === "stopped") {
startButton.disabled = false;
pauseButton.disabled = true;
resumeButton.disabled = true;
resetButton.disabled = true;
}
if (state === "running") {
startButton.disabled = true;
pauseButton.disabled = false;
resumeButton.disabled = true;
resetButton.disabled = false;
}
if (state === "paused") {
startButton.disabled = true;
pauseButton.disabled = true;
resumeButton.disabled = false;
resetButton.disabled = false;
}
}
JavaScript深掘り:UI と状態は「必ず同期させる」
状態(state)
→ ボタンの活性・非活性
→ どの操作が可能か
この流れを常に一致させることで、
アプリの操作性が一気に上がります。
setTimeout を使った「一時的な演出」を追加する
例:停止した瞬間に数字を赤くする
停止した瞬間に
「ピタッと止まった感」を出すために、
数字を一瞬赤くする演出を入れます。
function flashStop() {
timeDisplay.style.color = "red";
setTimeout(() => {
timeDisplay.style.color = "";
}, 300);
}
JavaScript停止処理に組み込みます。
function pause() {
if (state !== "running") return;
const now = Date.now();
remainingMs = endTime - now;
countdownEngine.stop();
state = "paused";
flashStop();
updateButtons();
}
JavaScript深掘り:setTimeout は「単発の演出」に最適
setInterval は“繰り返し”
setTimeout は“一回だけ遅らせる”
この違いを UI 演出に使うと、
アプリが一気にプロっぽくなります。
setInterval のズレを「視覚的に補正」する
setInterval は必ずズレる
setInterval(update, 10)
と書いても、
実際には 10ms ぴったりでは動きません。
ブラウザの負荷で遅れたり、
タブが非アクティブになると間隔が伸びたりします。
Date.now() で補正しているが…
ロジック上は補正できていますが、
UI では「カクつき」が見えることがあります。
解決策:数字の更新を“滑らかに見せる”
CSS の transition を使います。
#time {
transition: all 0.05s linear;
}
これだけで、
数字の変化がスムーズに見えるようになります。
深掘り:ロジックと UI は別物
ロジックは正確に
UI は滑らかに
という考え方が大事です。
入力チェックを強化して「事故」を防ぐ
カウントダウンの入力チェック
ユーザーが変な値を入れる可能性があります。
- 空欄
- 0
- マイナス
- 文字列
- 小数点
これらを防ぐために、
入力チェックを強化します。
function validateInput(value) {
const num = Number(value);
if (value.trim() === "") return "値を入力してください。";
if (Number.isNaN(num)) return "数字を入力してください。";
if (num <= 0) return "1以上の値を入力してください。";
if (!Number.isInteger(num)) return "整数を入力してください。";
return null;
}
JavaScript開始時に使います。
function startCountdown() {
if (state !== "stopped") return;
const error = validateInput(secondsInput.value);
if (error) {
showError(error);
return;
}
clearError();
// 残り時間の計算へ…
}
JavaScript深掘り:入力チェックは「アプリの安全装置」
入力チェックが甘いと、
- タイマーが即終了
- マイナス時間で暴走
- NaN が表示される
などの事故が起きます。
UI の安全性は
ロジックと同じくらい重要です。
状態管理を UI に反映する(色・ラベル変更)
状態に応じて色を変える
running → 緑
paused → オレンジ
stopped → グレー
など、視覚的に状態を伝えることができます。
function updateStateColor() {
if (state === "running") {
timeDisplay.style.color = "limegreen";
} else if (state === "paused") {
timeDisplay.style.color = "orange";
} else {
timeDisplay.style.color = "";
}
}
JavaScriptボタンのラベルも変える
resume ボタンを
「再開」→「続きから」
などに変えると、
ユーザーが迷いません。
setTimeout を使った「カウントダウン開始演出」
例:3 → 2 → 1 → START の演出
開始ボタンを押したら、
いきなり動き出すのではなく、
3秒カウントダウンしてから開始する演出を作れます。
function preStartCountdown(callback) {
let count = 3;
function tick() {
timeDisplay.textContent = count;
count--;
if (count < 0) {
callback();
return;
}
setTimeout(tick, 1000);
}
tick();
}
JavaScript開始ボタンでこう使います。
startButton.addEventListener("click", () => {
preStartCountdown(() => {
startCountdown();
});
});
JavaScript深掘り:setTimeout の再帰は「回数付きの繰り返し」に最適
setInterval では
「3回だけ実行」
がやりにくいですが、
setTimeout の再帰なら
「回数 × 間隔」を自由に作れます。
5日目のまとめコード(重要部分のみ)
let state = "stopped"; // "stopped" | "running" | "paused"
let endTime = 0;
let remainingMs = 0;
let timerId = null;
function startCountdown() {
if (state !== "stopped") return;
const error = validateInput(secondsInput.value);
if (error) {
showError(error);
return;
}
clearError();
remainingMs = Number(secondsInput.value) * 1000;
endTime = Date.now() + remainingMs;
timerId = setInterval(update, 10);
state = "running";
updateButtons();
updateStateColor();
}
function update() {
const now = Date.now();
remainingMs = endTime - now;
if (remainingMs <= 0) {
remainingMs = 0;
renderTime(remainingMs);
finish();
return;
}
renderTime(remainingMs);
}
function pause() {
if (state !== "running") return;
remainingMs = endTime - Date.now();
clearInterval(timerId);
state = "paused";
flashStop();
updateButtons();
updateStateColor();
}
function resume() {
if (state !== "paused") return;
endTime = Date.now() + remainingMs;
timerId = setInterval(update, 10);
state = "running";
updateButtons();
updateStateColor();
}
function reset() {
clearInterval(timerId);
remainingMs = 0;
endTime = 0;
state = "stopped";
renderTime(0);
updateButtons();
updateStateColor();
}
JavaScript今日いちばん深く理解してほしいこと
5日目の本質は、
「ロジックと UI を分けて考える」
ということです。
ロジック
→ 時間計算、状態管理、setInterval の制御
UI
→ ボタン制御、色、演出、入力チェック
この 2 つを分けて設計すると、
アプリが驚くほど扱いやすくなります。
そして、
setTimeout は「演出」
setInterval は「本体ロジック」
という役割分担を理解すると、
時間アプリの表現力が一気に広がります。
次の 6日目では、
「複数タイマーの同時管理」
や
「プリセットタイマー(25分タイマーなど)」
に挑戦していきます。
ここまで来たあなたなら、
もう“時間アプリを設計できる人”です。

