参考
javascript でストップウォッチを作ってみる。忘備録
完成形
シンプルなストップウォッチです.
HTML
最低限必要な表示する領域とボタンを配置します.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>StopWatch</title>
</head>
<body>
<div id="timer">00:00:000</div>
<button id="start">start</button> <button id="stop">stop</button>
<button id="reset">reset</button>
<script src="js/main.js"></script>
</body>
</html>
JavaScript(ES6)でロジックを書く
const timer = document.getElementById("timer");
const start = document.getElementById("start");
const stop = document.getElementById("stop");
const reset = document.getElementById("reset");
// 経過時間を保存する変数(単位:ミリ秒)
let elapsedTime;
// スタートボタンを押したときのUnix Epoch
let startTime;
// タイマーのID
let timerId;
// 以前 stop したタイミングまでの計測時間
let timeToAdd = 0;
// 表示される内容をアップデートする関数
const updateTimeText = () => {
// 1分 = 1000 ミリ秒 * 60秒
let m = Math.floor(elapsedTime / (1000 * 60));
// 1分に満たなかったミリ秒のうち,秒となったもの
let s = Math.floor((elapsedTime % (1000 * 60)) / 1000);
// 1秒になれなかったもの
let ms = elapsedTime % 1000;
// ゼロパディング
m = `0${m}`.slice(-2);
s = `0${s}`.slice(-2);
ms = `00${ms}`.slice(-3);
timer.textContent = `${m}:${s}:${ms}`;
};
// 経過時間の管理と計算を行う関数
const countUp = () => {
timerId = setTimeout(() => {
elapsedTime = Date.now() - startTime + timeToAdd;
updateTimeText();
countUp();
}, 10);
};
start.addEventListener("click", () => {
startTime = Date.now();
countUp();
});
stop.addEventListener("click", () => {
clearTimeout(timerId);
timeToAdd += Date.now() - startTime;
});
reset.addEventListener("click", () => {
elapsedTime = 0;
timeToAdd = 0;
// 00:00:000 を表示
updateTimeText();
});
ポイント
const,let
定数は const
,変数は let
を使います.
変数の意図しない変更を防ぐことができます.また,スコープが自身が定義されたブロックとなります.
アロー関数
毎度 function
と書くのは面倒です. () => {}
というように書くと見た目がすっきりします.
今回は関係ありませんが,this
の扱いが少し違うので注意が必要です.
What is this
value in JavaScript?
テンプレート文字列
"string text " + expression + " string text"
は, `string text ${expression} string text`
というように書くことができます.テンプレート文字列を使うことによってダブルクオートやシングルクオートを使う回数を減らすことができ,可読性が上がります.
デバッグ
上のコードにはバグがあるので修正します.
スタートボタンの連続クリック
このアプリケーションでは,連続で2回クリックをしたとき2つのタイマーイベント( countUp
)が発生してしまいます.
この問題を解決する方法としていくつかのアプローチがあります.
- スタートボタンとストップボタンを統合する
- 連続で押せないように無効化する
一般的な(物理)ストップウォッチでは,1 の選択肢が取られています.
ただ,手法としては 2 もあるのでどちらも紹介します.
スタートボタンとストップボタンを統合する
HTML
HTML はスタートボタンとストップボタンが統合されたことにより,button タグが一つ減ります.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>StopWatch</title>
</head>
<body>
<div id="timer">00:00:000</div>
<button id="start-stop">start</button> <button id="reset">reset</button>
<script src="js/main.js"></script>
</body>
</html>
JavaScript
現在の状態が動作中か停止中かを条件分けして,ボタンの処理を変える必要があります.
const timer = document.getElementById("timer");
const startStop = document.getElementById("start-stop");
const reset = document.getElementById("reset");
// 経過時間を保存する変数(単位:ミリ秒)
let elapsedTime;
// スタートボタンを押したときのUnix Epoch
let startTime;
// タイマーのID
let timerId = null;
// 以前 stop したタイミングまでの計測時間
let timeToAdd = 0;
// 表示される内容をアップデートする関数
const updateTimeText = () => {
// 1分 = 1000 ミリ秒 * 60秒
let m = Math.floor(elapsedTime / (1000 * 60));
// 1分に満たなかったミリ秒のうち,秒となったもの
let s = Math.floor((elapsedTime % (1000 * 60)) / 1000);
// 1秒になれなかったもの
let ms = elapsedTime % 1000;
// ゼロパディング
m = `0${m}`.slice(-2);
s = `0${s}`.slice(-2);
ms = `00${ms}`.slice(-3);
timer.textContent = `${m}:${s}:${ms}`;
};
// 経過時間の管理と計算を行う関数
const countUp = () => {
timerId = setTimeout(() => {
elapsedTime = Date.now() - startTime + timeToAdd;
updateTimeText();
countUp();
}, 10);
};
startStop.addEventListener("click", () => {
// タイマーIDが停止中
if (timerId === null) {
startTime = Date.now();
countUp();
// ボタンのテキストをstartからstopに更新
startStop.textContent = "stop";
} else {
// タイマーが動作中
clearTimeout(timerId);
timerId = null;
timeToAdd += Date.now() - startTime;
// ボタンのテキストをstopからstartに更新
startStop.textContent = "start";
}
});
reset.addEventListener("click", () => {
elapsedTime = 0;
timeToAdd = 0;
// 00:00:000 を表示
updateTimeText();
});
連続で押せないように無効化する
button タグには,disabled という属性を付けることができます. JavaScript から disabled 属性をコントロールすることによってボタンを無効化します.
HTML
はじめの HTML と同じコードですが,初期状態として stop ボタンを無効化しておきます.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>StopWatch</title>
</head>
<body>
<div id="timer">00:00:000</div>
<button id="start">start</button> <button id="stop" disabled>stop</button>
<button id="reset">reset</button>
<script src="js/main.js"></script>
</body>
</html>
JavaScript
はじめのコードに ボタンを有効化/無効化するコードを追加します.
const timer = document.getElementById("timer");
const start = document.getElementById("start");
const stop = document.getElementById("stop");
const reset = document.getElementById("reset");
// 経過時間を保存する変数(単位:ミリ秒)
let elapsedTime;
// スタートボタンを押したときのUnix Epoch
let startTime;
// タイマーのID
let timerId;
// 以前 stop したタイミングまでの計測時間
let timeToAdd = 0;
// 表示される内容をアップデートする関数
const updateTimeText = () => {
// 1分 = 1000 ミリ秒 * 60秒
let m = Math.floor(elapsedTime / (1000 * 60));
// 1分に満たなかったミリ秒のうち,秒となったもの
let s = Math.floor((elapsedTime % (1000 * 60)) / 1000);
// 1秒になれなかったもの
let ms = elapsedTime % 1000;
// ゼロパディング
m = `0${m}`.slice(-2);
s = `0${s}`.slice(-2);
ms = `00${ms}`.slice(-3);
timer.textContent = `${m}:${s}:${ms}`;
};
// 経過時間の管理と計算を行う関数
const countUp = () => {
timerId = setTimeout(() => {
elapsedTime = Date.now() - startTime + timeToAdd;
updateTimeText();
countUp();
}, 10);
};
start.addEventListener("click", () => {
startTime = Date.now();
countUp();
// スタートボタンを無効化
start.disabled = true;
// ストップボタンを有効化
stop.disabled = false;
});
stop.addEventListener("click", () => {
clearTimeout(timerId);
timeToAdd += Date.now() - startTime;
// スタートボタンを有効化
start.disabled = false;
// ストップボタンを無効化
stop.disabled = true;
});
reset.addEventListener("click", () => {
elapsedTime = 0;
timeToAdd = 0;
// 00:00:000 を表示
updateTimeText();
});
まとめ
プログラムは自分の書いたようにしか動きません. プログラムを書くにはまず必要な機能を考え言語化し,そのとおりにプログラムを書くと動きます.