JavaScript:正確なストップウォッチを作る

JavaScript:正確なストップウォッチを作る JavaScript

JavaScriptで以前作ったストップウォッチの挙動がおかしかったので修正した。
表示は、100分の1秒単位だが、内部的には1000分の1秒単位で計測している。実際に市販のストップウォッチと同時にスタートさせて1時間ほど測ってみたが、ずれは全くなかった。

↓タッチでサンプル実行
StopWatch2

DateクラスのgetTime()メソッドを利用して、スタート時刻との差分を求めて表示している。単純にsetTimeout(run, 1000)などとしても、1000ミリ秒毎に完全に呼ばれるわけではない。JavaScriptのMDNマニュアルでも、setTimeoutの第2引数に関して、「実際の呼び出しはこれより長くなる場合があります。」と記述されている。最初に作ったストップウォッチは、この方法で、1秒ごとにカウントしたが、3分も経過しないうちに30秒以上も狂った。

一式ダウンロード

ソースコード

HTML部分

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<link rel="stylesheet" href="mystyle.css">
	<script src="stopWatch.js"></script>
	<title>ストップウォッチ - Another Sky</title>
</head>
<body>
	<h1>ストップウォッチ</h1>
	<form name="form1" id="form1">
	<p id="time">
	00:00:00.00
	</p>
	<button type="button" id="btnStart" onclick="startStop()">
	スタート
	</button>
	<button type="button" id="btnClear" onclick="resetStopWatch()">
	クリア
	</button>
	</form>
	<footer>&copy; 2014 Shuichi Takeda.</footer>
</body>
</html>

CSS部分

@charset "utf-8";
*{
	margin: 0;
	padding: 0;
}
body{
	font-family: 'メイリオ', Osaka;
	margin: 2em;
}
h1{
	border-bottom:solid 1px #555;
}
p#time{
	font-size: 48pt;
	padding-top: 0.5em;
}
button{
	width:5em;
	padding:5pt;
	font-size: 20pt;
	border:solid 1px #555;
	border-radius: 12pt;
}
button:hover{
	background-color: pink;
}
footer{
	color: #ddd;
	background-color: transparent;
	text-align: right;
}

JavaScript部分

// -------------------------------------------------------------------------
// stopWatch.js ストップウォッチプログラム
//
//		1970年1月1日からの経過時間(ミリ秒単位)を使っている
//
// 					created at 2014-06-26 on torisky.com
// -------------------------------------------------------------------------

var mode;					// ストップウォッチのモード	RUN/STOP
var startTime;				// スタートした時刻
var nowTime;				// ストップした時刻
var addTime;				// 経過時間(ストップウォッチ再開時に加算する)
var millisec;					// 1000分の1秒
var sec100;					// 100分の1秒
var sec;						// 秒
var min;						// 分
var hour;					// 時
var gmt;						// タイムゾーンのオフセット値
							//	例)GMT+0900 なら 標準時より9時間後をさしているので-9する
var timerId;					// タイマー

/*
 * 定数
 */
var RUN = 1;				// 動作中
var STOP = 0;				// 停止中

/*
 * ストップウォッチのリセット
 */
function resetStopWatch(){
	mode = STOP;
	addTime = 0;
	millisec = sec100 = sec = min = hour = 0;
	gmt = new Date().getTimezoneOffset() / 60;	// 戻り値は分のため60で割る
	document.getElementById("time").innerHTML = "00:00:00.00";
}
		
/*
 * ボタン処理
 */
function startStop(){
	switch(mode){
		case STOP:		// スタートを押したとき
			mode = RUN;
			timerId = setTimeout(runStopWatch, 10);
			document.getElementById("btnClear").disabled = "true";	// クリアボタンを使用不可
			document.getElementById("btnStart").innerHTML = "ストップ";
			// スタート時刻を設定(ストップウォッチが進んでいれば加算)
			startTime = new Date().getTime();
			addTime = (hour*60*60*1000 + min*60*1000 + sec * 1000 + millisec);
			startTime -= addTime;
			break;

		case RUN:		// ストップを押したとき
			mode = STOP;
			clearTimeout(timerId);
//			nowTime = new Date().getTime();
			document.getElementById("btnStart").innerHTML = "スタート";
			document.getElementById("btnClear").disabled = "";		// クリアボタンを使用可
			drawTime();
	}
}

/*
 * 時間表示
 */
function drawTime(){
	var strTime = "";
	var strSec100, strSec, strMin, strHour;

	// 数値を文字に変換及び2桁表示設定
	strSec100 = "" + sec100;
	if ( strSec100.length < 2){
		strSec100 = "0" + strSec100;
	}
	strSec = "" + sec;
	if ( strSec.length < 2){
		strSec = "0" + strSec;
	}
	strMin = "" + min;
	if ( strMin.length < 2){
		strMin = "0" + strMin;
	}
	strHour = "" + hour;
	if ( strHour.length < 2){
		strHour = "0" + strHour;
	}
	// 表示形式を設定
	strTime = strHour + ":" + strMin + ":" + strSec + "." + strSec100;
	document.getElementById("time").innerHTML = strTime;
}

/*
 * 時間計測
 */
function runStopWatch(){
	// スタートからの差分をとる
	nowTime = new Date().getTime();
	diff = new Date(nowTime - startTime);
	// ミリ秒、100分の1秒、秒、分、時を設定
	millisec = diff.getMilliseconds();
	sec100 = Math.floor(millisec / 10);
	sec = diff.getSeconds();
	min = diff.getMinutes();
	hour = diff.getHours() + gmt;	// タイムゾーンのオフセットを考慮する

	drawTime();			// 時間表示
	timerId = setTimeout(runStopWatch, 10);
}

/*
 * 実行時の処理
 */
window.onload = function(){
	resetStopWatch();
}

コメント

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