さぁ!検索しよう!

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

DEMO

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

* {
    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;
}

JavaScript

(function() {
    var timer,
    i = 0,
    count = 0,
    time = document.getElementById('time'),
    lapWrap = document.getElementById('lap'),
    resetBtn = document.getElementById('reset'),
    startStop = document.getElementById('startstop');

    function digital(num) {
        var sec = num / 10,
        min = Math.floor(sec / 60),
        sec = (sec % 60).toFixed(1),
        hou = Math.floor(min / 60),
        min = min % 60;

        if (sec < 10) {
            sec = '0' + sec; //10秒(二桁)になるまで0を付けて二桁に
        }
        if (min < 10) {
            min = '0' + min;
        }
        if (hou < 10) {
            hou = '0' + hou;
        }
        return hou + ":" + min + ":" + sec;
    }

    time.innerText = digital(count);

    function stop() {
        clearInterval(timer);
    }

    function lap() {
        i++;
        var li = document.createElement('li');
        lapWrap.appendChild(li);
        li.innerHTML = 'LAP' + i + '&nbsp;&nbsp;' + '/' + '&nbsp;&nbsp;' + digital(count);
    }

    function switcher() {
        if (startStop.classList.contains('on')) {
            startStop.innerText = 'STOP';
            timer = setInterval(function() {
                count++;
                time.innerText = digital(count);
            }, 100);
        } else {
            startStop.innerText = 'START';
            resetBtn.innerText = 'RESET';
            stop();
        }
    }

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

    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();
        }
    }
})();

関数はdigital()stop()lap()、’switcher()’、reset()が定義されています。

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

digital(num)

function digital(num) {
    var sec = num / 10,
      min = Math.floor(sec / 60),
      sec = (sec % 60).toFixed(1),
      hou = Math.floor(min / 60),
      min = min % 60;
    if (sec < 10) {
      sec = '0' + sec; //10秒(二桁)になるまで0を付けて二桁に
    }
    if (min < 10) {
      min = '0' + min;
    }
    if (hou < 10) {
      hou = '0' + hou;
    }
    return hou + ":" + min + ":" + sec;
  }

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

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

sec

引数numに渡された値を10で割ることで、小数にしています。
numには、後述で登場するswitcher()内の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を付けておきます。

if (sec < 10) {
    sec = '0' + sec; //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()が実行されたときに実行される関数です。

function stop() {
    clearInterval(timer);
}

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

lap()

lap()はラップタイムを記録する関数です。

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

処理の流れ

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

switcher()

switcher()は、ボタンが押されたときに実行される関数です。

function switcher() {
    if (startStop.classList.contains('on')) {
        startStop.innerText = 'STOP';
        timer = setInterval(function() {
            count++;
            time.innerText = format(count);
        }, 100);
    } else {
        startStop.innerText = 'START';
        resetBtn.innerText = 'RESET';
        stop();
    }
}

処理の流れ

  1. タイムが止まっている状態でstartStopが押されると、startStopにonクラスが付いてタイムが動くため、ボタンの表記がstopに変わる。
    if (startStop.classList.contains('on')) {
        startStop.innerText = 'STOP';
    
  2. setIntervalで0.1秒毎にcountをインクリメントし、その数値をsetTime(count)に代入して実行したものをtime要素内に出力。
    timer = setInterval(function() {
        count++;
        time.innerText = setTime(count);
    }, 100);
    
  3. 動いている状態でstartStopが押されると、onクラスが外れてボタンの表記がSTARTに変わります。そして、stop()が実行され、タイムが一時停止します。
    else {
        startStop.innerText = 'START';
        resetBtn.innerText = 'RESET';
        stop();
    }
    

reset()

reset()は、resetBtnが押されたときに実行される関数です。

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

処理の流れ

  1. stop()が実行されてタイムが一時停止。
  2. startStopの表記がSTARTに変わる。
    startStop.innerText = 'START';
    
  3. startStopからonクラスが削除される。
    startStop.classList.remove('on');
    
  4. countに0を代入し、タイムをリセットする。

  5. リセットされたタイムをtime要素内に出力する。

    time.innerText = digital(count);
    
  6. lapWrap内を空にする。
    lapWrap.textContent = null;
    

クリックイベントは2つ用意しています。

startStop.onclick

startStopをクリックすると、スタート時はタイムをストップし、ストップ時はタイムをスタートされます。

処理の流れ

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

resetBtn.onclick

resetBtnをクリックすると、先程startStopで説明した3.によってlap()reset()が行われます。


参考文献

toFixedメソッド – Numberクラス – JavaScript入門

window.setInterval – Web API インターフェイス | MDN

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