JavaScript - ストップウォッチの作り方

JavaScript でストップウォッチを作ります。Date.now() を使用し経過時間を計算してミリ秒から時間、分、秒に変換する方法や、0 を付け足して数字の表示を二桁に揃えるゼロ埋めの方法などを学びましょう。

JavaScript - ストップウォッチの作り方
Photo by Ibrahim Yusuf / Unsplash

00 : 00 : 00 . 00 で表示するストップウォッチ

今回紹介するのは、JavaScript でシンプルなストップウォッチを作る方法です。0.01 秒ごとにカウントし、ミリ秒から時間までを二桁の数字で表示します。開始と停止は一つのボタンでコントロールできるようにコーディングします。

この記事を読むと分かること

  • Date.now()
  • 除算演算子 /
  • 剰余演算子 %
  • ゼロ埋め
  • setInterval()
  • clearInterval()
  • if…else

サンプルプロジェクト

最初に、ストップウォッチの動きを確認しておきましょう。

  • スタートボタンを押すと開始。ボタンの表示は STOP になる。
  • ストップボタンを押すと停止。ボタンの表示は START になる。
  • 再びスタートボタンを押すと、前の経過時間に引き続き開始する。
  • リセットボタンを押すと表示時間がゼロになる。
ストップウォッチのサンプルプロジェクト
ストップウォッチのサンプルプロジェクト

HTML

まずは HTML です。今回は開始と停止を一つのボタンで操作するので、作成するボタンは二つです。

  • 時間を表示する stopwatch <div> を用意します。
  • toggle-button <button> は、スタートとストップを切り替えるボタンです。
  • reset-button <button> は、ストップウォッチをゼロにリセットするボタンです。
<!-- ストップウォッチ(時間の表示) -->
<div class="stopwatch">00 : 00 : 00 . 00</div>

<div class="button-container">
  <!-- スタートとストップを切り替えるボタン -->
  <button class="toggle-button button">START</button>
  <!-- リセットボタン -->
  <button class="reset-button button">RESET</button>
</div>

JavaScript

続いて JavaScript です。JavaScript では関数を四つ定義します。また、時間を計算・変換するために、除算演算子 /剰余演算子 % を使用します。計算する文が多くコードも長めですが、一つずつ順番に解説していきますね。

要素を取得する

最初に、JavaScript で操作したい HTML 要素を querySelector() メソッドで取得します。それぞれ変数に格納しておきましょう。

// ストップウォッチ(時間の表示)
const stopwatch = document.querySelector('.stopwatch');
// 切り替えボタン(スタートorストップ)
const toggleBtn = document.querySelector('.toggle-button');
// リセットボタン
const resetBtn = document.querySelector('.reset-button');

変数を宣言する

次に行うのは、変数の宣言です。

  • 開始時間を格納するための変数 startTime を用意します。
  • 経過時間を格納する変数を elapsedTime とし、0 で初期化します。
  • 一定間隔で時間の表示を更新するためにタイマーを使います。そのタイマーを入れておくための変数 intervalTimer を用意しておきます。この変数は、タイマーを停止するときに必要になります。
// 開始時間
let startTime;
// 経過時間
let elapsedTime = 0;
// タイマー
let intervalTimer;

ストップウォッチを開始する

ここからは、ストップウォッチを開始する関数 start を定義していきます。start は、一定間隔で時間の表示を更新する関数です。

// ストップウォッチを開始する関数
function start() {

  // 0.01秒間隔のタイマーを設定する
  // 0.01秒ごとに経過時間を計算する
  // 経過時間を「時間」「分」「秒」「二桁のミリ秒」に変換する
  // 0.01秒ごとにストップウォッチの表示を更新する

}

0.01 秒間隔のタイマーを設定する

まずは、タイマーを設定します。指定した時間間隔で何かを定期的に実行したいときに使うのは、setInterval() メソッドです。

  • あとでタイマーを停止できるように、変数 intervalTimer を使用します。
  • 0.01 秒間隔で時間の表示を更新したいので、遅延間隔を 10 ミリ秒とします。
function start() {

  // 0.01秒間隔のタイマーを設定する
  intervalTimer = setInterval(function() {

    // 0.01秒ごとに経過時間を計算する
    // 経過時間を「時間」「分」「秒」「二桁のミリ秒」に変換する
    // 0.01秒ごとにストップウォッチの表示を更新する
    // …
    
  }, 10);

}

0.01 秒ごとに経過時間を計算する

次に、setInterval() メソッドで実行する処理を書いていきましょう。

時間の表示を更新するためには、0.01 秒ごとに経過時間を計算する必要があります。そのために使うのが、Date.now() メソッドです。Date.now() を使うと、UTC1970年 1 月 1 日 0 時 0 分 0 秒 からの経過時間を取得することができます。この経過時間はタイムスタンプといい、単位はミリ秒です。

Date.now() の例:

// 現在のタイムスタンプ
Date.now(); //1716860077437(ミリ秒)

経過時間を計算する文は以下のようになります。

  • 現在のタイムスタンプ Date.now() から開始時間 startTime を引いて、ストップウォッチをスタートさせてからの経過時間を計算します。
  • 割り出した経過時間は、変数 elapsedTime に格納します。
function start() {
  intervalTimer = setInterval(function() {

    // 0.01秒ごとに経過時間を計算する
    elapsedTime = Date.now() - startTime;

    // 経過時間を「時間」「分」「秒」「二桁のミリ秒」に変換する
    // 0.01秒ごとにストップウォッチの表示を更新する
    // …
    
  }, 10);
}

最初にスタートボタンを押した瞬間は、Date.now() の値と変数 startTime の値が同じになるので、経過時間 elapsedTime の値は 0 です。この文は、setInterval() メソッドで 0.01 秒ごとに繰り返し実行され、 その度に Date.now() で返る値が変わるため、経過時間も変化します。

経過時間を「時間」「分」「秒」「二桁のミリ秒」に変換する

上の文で割り出した経過時間 elapsedTime の値はミリ秒です。このままの値だとストップウォッチとして表示できませんね。「00時間:00分:00秒 . 00ミリ秒」の形式で表示できるよう、それぞれの値に変換しましょう。

Step 1:「ミリ秒」から「時間」を計算する

経過時間ミリ秒から「時間」を計算し、変数 hours に格納します。考え方は次のとおりです。

  • 1時間は、3,600,000 ミリ秒(1,000 ミリ秒 × 60 秒 × 60 分)です。
  • 「ミリ秒」から「時間」を算出するには、経過時間 elapsedTime3600000 で割ります。

「ミリ秒」から「時間」を計算する式:

// 経過時間 / (1,000ミリ秒 × 60秒 × 60分)
elapsedTime / (1000 * 60 * 60)
// または
elapsedTime / 3600000

たとえば、経過時間 elapsedTime4000000 という値が格納されているとします。計算結果は 1.11111111 となり、少数です。割り出した値を「時間」として表示できるように、Math.floor() メソッドで整数にしてから変数に格納します。

// ミリ秒から時間を割り出して整数にする
let hours = Math.floor(elapsedTime / (1000 * 60 * 60));

経過時間ミリ秒から「時間」を計算し、整数にした値を変数 hours に格納する文ができました。この文は、下のように割り算で表すこともできます。今回は、こちらの書き方を使ってコーディングしていきますね。

// 経過時間 / 1,000ミリ秒 / 60秒 / 60分
let hours = Math.floor(elapsedTime / 1000 / 60 / 60);

Step 2:「ミリ秒」から「分」を計算する

経過時間ミリ秒から「分」を計算し、変数 minutes に格納します。

  • 1分は、60,000 ミリ秒(1,000 ミリ秒 × 60 秒 )です。
  • 「ミリ秒」から「分」を算出するには、経過時間 elapsedTime100060 で割ります。
  • 60 分を超えたら「時間」になるので、剰余演算子 % を使って 60 で割った余りだけを「分」とします。
// (経過時間 / 1,000ミリ秒 / 60秒) % 60分
let minutes = Math.floor((elapsedTime / 1000 / 60) % 60);

Step 3:「ミリ秒」から「秒」を計算する

経過時間ミリ秒から「秒」を計算し、変数 seconds に格納します。

  • 1秒は、1,000 ミリ秒です。
  • 「ミリ秒」から「秒」を算出するには、経過時間 elapsedTime1000 で割ります。
  • 60 秒を超えたら「分」になるので、 % を使って 60 で割った余りだけを「秒」とします。
// (経過時間 / 1,000ミリ秒) % 60秒
let seconds = Math.floor((elapsedTime / 1000) % 60);

Step 4:「ミリ秒」の二桁を取得する

最後に「ミリ秒」です。二桁のみを取得して、変数 milliseconds に格納します。

  • 1,000 ミリ秒を超えたら「秒」になるので、% を使って 1000 で割った余りだけを「ミリ秒」とします。
  • ミリ秒は「999」までの三桁となりますが、今回は二桁で表示させたいので、10 で割ります。
// (経過時間 % 1,000ミリ秒) / 10->二桁
let milliseconds = Math.floor((elapsedTime % 1000) / 10);

以上で、経過時間ミリ秒の値を変換する文ができました。

function start() {
  intervalTimer = setInterval(function() {
    elapsedTime = Date.now() - startTime;

    // 経過時間を「時間」「分」「秒」「二桁のミリ秒」に変換する
    let hours = Math.floor(elapsedTime / 1000 / 60 / 60);
    let minutes = Math.floor((elapsedTime / 1000 / 60) % 60);
    let seconds = Math.floor((elapsedTime / 1000) % 60);
    let milliseconds = Math.floor((elapsedTime % 1000) / 10);

    // 0.01秒ごとにストップウォッチの表示を更新する
    // …
    
  }, 10);
}

0.01 秒ごとにストップウォッチの表示を更新する

経過時間を「時間」「分」「秒」「二桁のミリ秒」で取得できたら、その値を使ってストップウォッチの表示を更新しましょう。

// ストップウォッチの表示を更新する
stopwatch.textContent = `${hours} : ${minutes} : ${seconds} . ${milliseconds}`;

` ${ } ` を使って文字列を表すテンプレートリテラルについてはこちらの記事で紹介しているので参考にしてください。

JavaScript - Date オブジェクトで日付と時刻を取得する方法
Date オブジェクトで現在日時を取得する方法を紹介します。getMonth() や getHours() など日時に関するメソッドを使って、今日の日付や現在の時刻を表示できるようになりましょう。

たとえば、「2 時間 34 分 5 秒 66 ミリ秒」の場合、上の文に当てはめると、表示は「2 : 34 : 5 . 66」となります。今回は、それぞれの数字を二桁にして「02 : 34 : 05 . 66」のように表示したいので、関数 pad() を使って下のような文にしますよ。関数 pad() については、このあと説明しますね。

function start() {
  intervalTimer = setInterval(function() {
    elapsedTime = Date.now() - startTime;
    let hours = Math.floor(elapsedTime / 1000 / 60 / 60);
    let minutes = Math.floor((elapsedTime / 1000 / 60) % 60);
    let seconds = Math.floor((elapsedTime / 1000) % 60);
    let milliseconds = Math.floor((elapsedTime % 1000) / 10);

    // 0.01秒ごとにストップウォッチの表示を更新する
    stopwatch.textContent = `${pad(hours)} : ${pad(minutes)} : ${pad(seconds)} . ${pad(milliseconds)}`;

  }, 10);
}

以上で、ストップウォッチを開始して 0.01 秒ごとに時間の表示を更新する関数 start を定義できました。

ゼロ埋めして桁を揃える関数

さて、先ほど使用した関数 pad() ですが、これは、文字列の桁数を揃えるための関数です。「2 時間」を「02 時間」とするように「0」を付け足すことを、ゼロ埋め(ゼロパディング)といいます。今回は二桁に揃えたいので、関数 pad() は次のように定義することができます。

// ゼロ埋めする関数
function pad(number) {
  return number.toString().padStart(2, '0');
}    

ゼロ埋めについてはこちらの記事で詳しく紹介しているので参考にしてください。

JavaScript - デジタル時計の作り方
Date オブジェクトを使ってデジタル時計を作ります。setInterval() で1秒ごとに時間を更新する方法や、表示する桁数を揃えるゼロパディングの方法、アロー関数 => の書き方について学びましょう。

開始と停止を切り替える

さて今度は、開始と停止を切り替えるための関数 startStop を定義していきますよ。今回はストップウォッチの開始と停止を一つのボタンで操作するので、その切り替えを実行する関数です。

// 開始と停止を切り替える関数
function startStop() {

  // if...else文で条件分岐する
  // 開始時間を計算する
  // ストップウォッチを開始してボタンの表示をSTOPにする
  // タイマーを停止してボタンの表示をSTARTにする

}

if...else 文で条件分岐する

開始と停止の処理を分けるために使うのは、if…else文です。

  • もしボタンの表示が START ならストップウォッチを開始し、そうでなければ停止します。
function startStop() {

  // if...else文で条件分岐する
  if (toggleBtn.textContent === 'START') {

    //*** ストップウォッチを開始する処理 ***
    // 開始時間を計算する
    // ストップウォッチを開始してボタンの表示をSTOPにする

  } else {

    //*** ストップウォッチを停止する処理 ***
    // タイマーを停止してボタンの表示をSTARTにする

  }
}

開始時間を計算する

ストップウォッチを開始するときの処理を書いていきましょう。

まずは、開始時間を計算して値を取得します。変数 startTime には、一旦停止してから再びスタートボタンを押したときのことも考えて計算した値を格納しなければいけません。なぜ単純に開始時間を現在のタイムスタンプ startTime = Date.now() にしてはいけないかというと、それだと再開したときに経過時間がゼロになってしまうからです。

  • Date.now() で取得した値からそれまでの経過時間 elapsedTime を引いた値を変数 startTime に格納します。
function startStop() {
  if (startStopBtn.textContent === 'START') {

    // 開始時間を計算する
    startTime = Date.now() - elapsedTime;

    // ストップウォッチを開始してボタンの表示をSTOPにする
    // …
    
  } else {

    // タイマーを停止してボタンの表示をSTARTにする
    // …
    
  }
}

最初にスタートボタンを押したときは経過時間 elapsedTime の値は 0 なので、変数 startTime には単純に現在のタイムスタンプが入ります。

ストップウォッチを開始してボタンの表示を STOP にする

開始時間を取得できたら、ストップウォッチを開始して時間が更新されるようにしましょう。

  • 先ほど定義した関数 start を実行してストップウォッチを開始します。
  • ボタンの表示を STOP に切り替えます。
function startStop() {
  if (toggleBtn.textContent === 'START') {
    startTime = Date.now() - elapsedTime;

    // ストップウォッチを開始する
    start();
    // ボタンの表示をSTOPにする
    toggleBtn.textContent = 'STOP';

  } else {

    // タイマーを停止してボタンの表示をSTARTにする
    // …
    
  }
}

タイマーを停止してボタンの表示を START にする

続いて、ストップウォッチを停止する処理です。else の部分に書いていきますよ。

  • clearInterval() メソッドで変数 intervalTimer を指定し、ストップウォッチを更新するタイマーを停止します。
  • ボタンの表示を START に切り替えます。
function startStop() {
  if (toggleBtn.textContent === 'START') {
    startTime = Date.now() - elapsedTime;
    start();
    toggleBtn.textContent = 'STOP';

  } else {

    // タイマーを停止する
    clearInterval(intervalTimer);
    // ボタンの表示をSTARTにする
    toggleBtn.textContent = 'START';

  }
}

以上で、ストップウォッチを開始または停止する関数 startStop を定義できました。

ストップウォッチをリセットする

開始と停止を実行する関数を用意できたら、残るはストップウォッチをゼロにリセットするための関数 reset です。この関数の内容はとてもシンプルです。

  • タイマーを停止します。
  • 経過時間を 0 にします。
  • ストップウォッチの表示を 00 : 00 : 00 . 00 にします。
  • ボタンの表示を START にします。
// ストップウォッチをリセットする関数
function reset() {
  clearInterval(intervalTimer);
  elapsedTime = 0;
  stopwatch.textContent = '00 : 00 : 00 . 00';
  toggleBtn.textContent = 'START';
}

ボタンにクリックイベントを追加する

最後に、二つのボタンに click イベントを追加しましょう。ユーザーがボタンをクリックしてストップウォッチを操作できるようにしますよ。

  • スタートまたはストップボタンを押したら、ストップウォッチを開始または停止する関数 startStop を呼び出します。
  • リセットボタンを押したら、ストップウォッチをリセットする関数 reset を呼び出します。
toggleBtn.addEventListener('click', startStop);
resetBtn.addEventListener('click', reset);

完成コード

ストップウォッチのコードが完成です。

//************************
// ストップウォッチのコード
//************************

const stopwatch = document.querySelector('.stopwatch');
const toggleBtn = document.querySelector('.toggle-button');
const resetBtn = document.querySelector('.reset-button');

let startTime;
let elapsedTime = 0;
let intervalTimer;

// ストップウォッチを開始する
function start() {
  intervalTimer = setInterval(function() {
    elapsedTime = Date.now() - startTime;
    let hours = Math.floor(elapsedTime / 1000 / 60 / 60);
    let minutes = Math.floor((elapsedTime / 1000 / 60) % 60);
    let seconds = Math.floor((elapsedTime / 1000) % 60);
    let milliseconds = Math.floor((elapsedTime % 1000) / 10);
    stopwatch.textContent = `${pad(hours)} : ${pad(minutes)} : ${pad(seconds)} . ${pad(milliseconds)}`;
  }, 10);
}

// 前に0を付け足して二桁に揃える
function pad(number) {
  return number.toString().padStart(2, '0');
}

// 開始と停止を切り替える
function startStop() {
  if (toggleBtn.textContent === 'START') {
    startTime = Date.now() - elapsedTime;
    start();
    toggleBtn.textContent = 'STOP';
  } else {
    clearInterval(intervalTimer);
    toggleBtn.textContent = 'START';
  }
}

// ストップウォッチをリセットする
function reset() {
  clearInterval(intervalTimer);
  elapsedTime = 0;
  stopwatch.textContent = '00 : 00 : 00 . 00';
  toggleBtn.textContent = 'START';
}

// ボタンにクリックイベントを追加する
toggleBtn.addEventListener('click', startStop);
resetBtn.addEventListener('click', reset);

See the Pen JavaScript - Stopwatch by Pyxofy (@pyxofy) on CodePen.


まとめ

今回は、JavaScript でストップウォッチを作る方法を紹介しました。

経過時間を計算するために使用したのが Date.now() メソッドです。時間は二桁にゼロ埋めして、桁数を揃えて表示されるようにしました。ミリ秒から他の単位に変換する計算は、演算子や数字がたくさん出てきて少し難しかったかもしれませんね。開始ボタンと停止ボタンは別で作っても良いですが、一つのボタンで開始と停止を切り替えられるようにしたところも、今回のコードのポイントでした。

最後まで読んでいただき、ありがとうございます。この記事をシェアしてくれると嬉しいです!

SNSで Pyxofy とつながりましょう! LinkedInThreadsMastodon X (Twitter) @pyxofyFacebook

関連記事

JavaScript - クリックカウンターの作り方【初心者向け】
ボタンのクリック数を画面に表示するクリックカウンターを作ります。コードのポイントとなるのはインクリメント演算子 ++ と innerHTML です。プログラミング初心者の方でも分かりやすいように、簡単な HTML と JavaScript で解説します。
CSS Art - How to Make a Game Character - Super Mario
A plumber jumping in and out of pipes, throwing fireballs while rescuing a princess. Step-by-step article to create a world renowned game character.
CSS Animation – Tokyo Tower Neon Sign
Tokyo’s skyline is iconic, mixing the traditional and high-tech. Let’s make a CSS-animated Tokyo Tower neon sign in this step-by-step article.
スクラッチプログラミング - パックマンみたいなゲームのつくりかた - Part 1
パックマンがたべるドットをきれいにならべるプログラムを紹介(しょうかい)します。迷路(めいろ)の背景(はいけい)をつくって、通路(つうろ)に等間隔(とうかんかく)でドットを表示(ひょうじ)しましょう。