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

JavaScript
スポンサーリンク

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

中級編の 1 日目は
「タイマー & ストップウォッチの基礎ロジックを完全に理解する」
ことがテーマです。

今日扱うキーワードは次の 6 つ。

setTimeout
setInterval
時間管理ロジック
開始
停止
リセット
ミリ秒表示
多重起動防止

これらはタイマー系アプリの“心臓部”です。
ここを理解すると、カウントダウンタイマー、ストップウォッチ、ポモドーロタイマーなど、
あらゆる時間アプリが作れるようになります。


setTimeout と setInterval の違いを「体感」で理解する

setTimeout は「一回だけ遅らせて実行」

setTimeout(() => {
  console.log("1秒後に1回だけ実行");
}, 1000);
JavaScript

1000ms(1秒)後に 1回だけ 実行されます。

setInterval は「一定間隔で繰り返し実行」

setInterval(() => {
  console.log("1秒ごとに繰り返し実行");
}, 1000);
JavaScript

1000ms ごとに 何度も 実行されます。

深掘り:タイマーアプリでは setInterval が主役

ストップウォッチは
「0.01秒ごとに時間を更新する」
という動きが必要なので、
setInterval が圧倒的に使いやすいです。

ただし、
setInterval は“止めない限り永遠に動く”
というクセがあるため、
停止処理や多重起動防止が必須になります。


ストップウォッチの「時間管理ロジック」を理解する

ストップウォッチは「経過時間」を管理するアプリ

ストップウォッチの本質は、

スタートした時刻を覚えておき、
現在時刻との差を計算する

というだけです。

例として、スタートした瞬間の時刻を記録します。

let startTime = Date.now();
JavaScript

1秒後にこう計算できます。

const elapsed = Date.now() - startTime;
JavaScript

これで「何ミリ秒経ったか」がわかります。

深掘り:Date.now() は“ミリ秒精度の時計”

Date.now() は
「1970年からの経過ミリ秒」を返す関数です。

これを使うと、

  • ミリ秒単位のストップウォッチ
  • 精度の高いカウントダウン
  • 時間差の計算

がとても簡単にできます。


ミリ秒 → 分・秒・ミリ秒に変換する

変換ロジック

例えば 12,345ms を表示したいとします。

const ms = elapsed % 1000;
const totalSeconds = Math.floor(elapsed / 1000);
const seconds = totalSeconds % 60;
const minutes = Math.floor(totalSeconds / 60);
JavaScript

これで、

minutes → 分
seconds → 秒
ms → ミリ秒

が取れます。

表示を整える(ゼロ埋め)

function pad(num, size) {
  return num.toString().padStart(size, "0");
}
JavaScript

表示例:

`${pad(minutes, 2)}:${pad(seconds, 2)}.${pad(ms, 3)}`
JavaScript

深掘り:ミリ秒表示は「桁数が命」

ミリ秒は 0〜999 なので、
必ず 3 桁で表示しないと見た目が崩れます。


setInterval を使ったストップウォッチの基本構造

コードの骨格

let startTime = 0;
let timerId = null;

function start() {
  startTime = Date.now();
  timerId = setInterval(update, 10); // 0.01秒ごと
}

function update() {
  const elapsed = Date.now() - startTime;
  display(elapsed);
}

function stop() {
  clearInterval(timerId);
}

function reset() {
  clearInterval(timerId);
  display(0);
}
JavaScript

深掘り:update 関数は「画面を更新するだけ」

update の役割は、

  • 経過時間を計算
  • 表示を更新

この 2 つだけです。

ロジックを分離することで、
コードが読みやすくなり、バグも減ります。


多重起動防止の考え方

なぜ必要なのか?

start ボタンを連打すると、
setInterval が何個も動き始めてしまいます。

すると、

  • 時間が2倍速・3倍速で進む
  • 停止しても止まらない
  • リセットしても動き続ける

などの“タイマー暴走”が起きます。

防止ロジック

let isRunning = false;

function start() {
  if (isRunning) return; // すでに動いているなら何もしない
  isRunning = true;

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

function stop() {
  clearInterval(timerId);
  isRunning = false;
}
JavaScript

深掘り:状態管理(isRunning)がアプリを安定させる

タイマー系アプリは
「今動いているかどうか」を
必ず状態として持つ必要があります。

これを怠ると、
ほぼ確実にバグります。


1日目の完成コード(シンプル版)

let startTime = 0;
let timerId = null;
let isRunning = false;

function start() {
  if (isRunning) return;
  isRunning = true;

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

function update() {
  const elapsed = Date.now() - startTime;
  display(elapsed);
}

function stop() {
  clearInterval(timerId);
  isRunning = false;
}

function reset() {
  clearInterval(timerId);
  isRunning = false;
  display(0);
}

function display(ms) {
  const milliseconds = ms % 1000;
  const totalSeconds = Math.floor(ms / 1000);
  const seconds = totalSeconds % 60;
  const minutes = Math.floor(totalSeconds / 60);

  const text =
    `${String(minutes).padStart(2, "0")}:` +
    `${String(seconds).padStart(2, "0")}.` +
    `${String(milliseconds).padStart(3, "0")}`;

  document.getElementById("time").textContent = text;
}
JavaScript

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

setInterval は「繰り返し実行するエンジン」

ストップウォッチは
“一定間隔で update を呼び続ける”
という仕組みで動いています。

Date.now() は「経過時間を正確に測るための時計」

setInterval のズレを補正するためにも、
Date.now() を使って「差分」で時間を計算するのが重要です。

多重起動防止は「状態管理」がすべて

isRunning を使うことで、
start の暴走を完全に防げます。


2日目へのつなぎ

2日目はここからさらに、

ラップ機能
一時停止と再開
カウントダウンタイマー
アニメーション的な表示改善

など、
“タイマーアプリとしての完成度”を上げていきます。

今日の内容をしっかり理解できたあなたなら、
もう中級タイマーアプリの基礎は完璧です。

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