さぁ!検索しよう!

簡単なコードでラップ機能付きストップウォッチを作る方法です。

DEMO

HTML

始めに以下のHTMLを書きます。

<div id="wrap">
    <div id="output">
        <div id="time"></div>
    </div>
    <ul id="btns" class="clearfix">
        <li id="startstop">START</li>
        <li id="reset">RESET</li>
    </ul>
    <ul id="lap" class="clearfix">
    </ul>
</div>

ポイント
タイム<div id="time"></div>とラップ<ul id="lap" class="clearfix"></ul>内はJavaScriptで生成した要素を挿入するため、ここでは空の状態としています。

CSS

次に以下のCSSを書きます。

* {
    box-sizing: border-box;
}

body {
    background: #08233E;
    color: #fff;
    font-family: 'Orbitron', sans-serif;
    margin: 0;
    text-align: center;
}

.clearfix:after {
    content: "";
    clear: both;
    display: block;
}

#output {
    background: #0C555D;
    font-size: 44px;
    padding: 20px 0;
}

ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

#btns li {
    /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#7d7e7d+0,242424+100 */

    background: #7d7e7d;
    /* Old browsers */

    background: -moz-linear-gradient(top, #7d7e7d 0%, #242424 100%);
    /* FF3.6-15 */

    background: -webkit-linear-gradient(top, #7d7e7d 0%, #242424 100%);
    /* Chrome10-25,Safari5.1-6 */

    background: linear-gradient(to bottom, #7d7e7d 0%, #242424 100%);
    /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */

    filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#7d7e7d', endColorstr='#242424', GradientType=0);
    /* IE6-9 */

    cursor: pointer;
    float: left;
    padding: 20px 0;
    width: 50%;
}

#btns li:first-child {
    border-right: 1px solid #333;
}

#lap li {
    border-bottom: 1px solid #2C5379;
    padding: 15px 0;
}

CSSではストップウォッチの見た目を作っています。

JavaScript

最後に以下のJavaScriptを書きます。

(() => {
    let timer,
        i = 0,
        count = 0;
    const time = document.getElementById('time'),
          lapList = document.getElementById('lap'),
          resetBtn = document.getElementById('reset'),
          startStop = document.getElementById('startstop');

    function format(num) {
        let sec = num / 10;
        let min = Math.floor(sec / 60);
        sec = (sec % 60).toFixed(1);
        let hour = Math.floor(min / 60);
        min = min % 60;

        if (sec < 10) {
            sec = `0${sec}`; //10秒(二桁)になるまで0を付けて二桁に
        }
        if (min < 10) {
            min = `0${min}`;
        }
        if (hour < 10) {
            hour = `0${hour}`;
        }

        return `${hour}:${min}:${sec}`;
    }

    function print() {
        time.innerText = format(count);
    }

    function stop() {
        clearInterval(timer);
    }

    function lap() {
        i++;
        const li = document.createElement('li');
        lapList.appendChild(li);
        li.innerHTML = 'LAP' + i + '&nbsp;&nbsp;' + '/' + '&nbsp;&nbsp;' + format(count);
    }

    function switcher() {
        if (startStop.classList.contains('on')) {
            startStop.innerText = 'STOP';

            timer = setInterval(() => {
                count++;
                time.innerText = format(count);
            }, 100);
        } else {
            startStop.innerText = 'START';
            resetBtn.innerText = 'RESET';
            stop();
        }
    }

    function reset() {
        stop();
        startStop.innerText = 'START';
        startStop.classList.remove('on');
        count = 0;
        time.innerText = format(count);
        lapList.textContent = null;
    }

    print();

    startStop.onclick = function() {
        this.classList.toggle('on');
        resetBtn.innerText = 'LAP';
        resetBtn.classList.toggle('on');
        switcher();
    }

    resetBtn.onclick = function() {
        if (this.classList.contains('on')) {
            lap();
        } else {
            reset();
        }
    }
})();

JavaScriptでは以下のことを行っています。

format関数を定義する

format関数は引数に渡された値countを、デジタル表記(00:00:00.0)に変換する関数です。

デジタル表記は時間:分:秒となり、時間はhour、分はmin、秒はsecとしています。

secminhourはそれぞれ以下のようにして求めます。

sec
numを10で割ることで、小数にしています。numには、後に登場するtimerによって、100ミリ秒つまり0.1秒毎に1カウントされたcountが次々と代入されていきます。

しかし、このままでは初期表示が00:00:00で、STARTを押したときにいきなり00:00:00.0と表示されるため不自然な表示となります。

このようなことを防ぐために、事前にsecを60で割った余りに対してtoFixed(1)を実行し、小数点以下第1位までの表記にします。これによって初期表示から00:00:00.0となります。

続けて、(sec%60)(secを60で割った余り)とすることで、小数点超えのカウント表示(00.0の00の部分)を0以上60未満とし、60以上をカウント表示させないようにしています。なので、60に到達したら0に戻し、そこからまた60までカウントしていきます。

これは、一般に私たちが使っているストップウォッチは70,80…と表示されないことや、小数点超えのカウント表示を二桁までに保つための処理でもあります。

最後に、小数点以上の表記の前に、10秒つまり二桁になるまで0を付けておきます。

min
minは分を示し、デジタル表記の中央00の部分です。1分は60個の1秒なので(で成り立っているので)、秒secを60で割り、60個に分けています。更に、Math.floorで小数点以下を削除しています。
更に、前述のsecと同様に、minmin%60を上書きすることで、カウントの表記を00以上60未満としています。
また、これも前述の
sec`と同様に、小数点以上の表記の前に、10分つまり二桁になるまで0を付けておきます。

hour
hourは時間を示し、デジタル表記の左端00の部分です。1時間は60個の1分でできているので、1時間は60分なので、1分/60分つまり60分のうちの1分で、min/60となります。更に、Math.floorで小数点以下を削除しています。

stop関数を定義する

タイマーを止めるstop関数を定義します。この関数はストップボタンかリセットボタンが押されたときに実行されます。

switcher()が実行されるとタイムを一時停止し、reset()が実行されると停止します。

lap関数を定義する

ラップタイムを記録するlap関数を定義します。

処理の流れ
1. iはLAPの回数を表しており、lap関数が実行される度にインクリメントされ、LAP1,LAP2…と記録されていく。
2. document.createElement('li');でli要素を生成し、lapList内に挿入する。
3. 生成したli要素内に、LAPの回数とlap関数が実行されたときのタイムformatを挿入する。

switcher関数を定義する

switcher関数を定義します。この関数はstartStopを押したときに各ボタンの表記をどうすべきかを決めます。

処理の流れ
1. タイムが止まっている状態でstartStopが押されると、startStop.onクラスが付いてタイムが動くため、ボタンの表記がstopに変わる。
2. setIntervalで0.1秒毎にcountをインクリメントし、その数値を#time内に挿入。
3. 動いている状態でstartStopが押されると、.onクラスが外れてボタンの表記がSTARTに変わります。そして、stop関数が実行され、タイムが一時停止します。

reset関数を定義する

ストップウォッチをリセットするreset関数を定義します。

処理の流れ
1. stop関数が実行されてタイムが一時停止。
2. startStopの表記がSTARTに変わる。
3. startStopから.onクラスが削除される。
4. countに0を代入し、タイムをリセットする。
5. リセットされたタイムを#time要素内に出力する。
6. lapList内を空にする。

ストップウォッチを操作できるようにする

最後にonClickイベントにコールバック関数を登録することでストップウォッチが操作できるようになります。

startStopが押されると実行されるコールバック関数では以下のことをしています。

処理の流れ
1. startStop.onクラスが付いていれば外し、付いていなければ付けます。
2. RESETの表記をLAPに切り替えます。
3. resetBtn.onクラスが付いていれば外し、付いていなければ付けます。この処理は、後に登場するresetBtnのクリックイベントに関わります。
4. switcher関数を実行し、1.によってタイムのスタート⇔ストップが行なわれます。

resetBtnが押されると実行されるコールバック関数では先程startStopで説明した3.によってlap関数の実行orreset関数の実行が行われます。

さいごに

以上で、JavaScriptでラップ機能付きのストップウォッチを作る方法を終わります。

参考文献