WEBMAN

HTML,CSS,JSでハマった事のメモとWEB制作の実験場

更新日: / 公開日:

Vue.js でシンプルなストップウォッチを作ってみた

Vue.js でシンプルなストップウォッチを作ってみた

SPONSOR

お疲れ様です、ナガです。

別に Vue.js である必要は無いけど勉強がてら作ってみました。

今回学べたこと

  • 時間の計算方法(今更だけど…)
  • 一定間隔で繰り返す処理(window.requestAnimationFramewindow.cancelAnimationFrame
  • performance.now() の使い方
  • computed の使い方
  • filters の使い方

使用したモジュールとか

  • Vue.js
  • Semantic UI(bootstrap よりもスッキリしてて好きです。cssのみ使いました)

日付計測処理

正直、時間の計算方法が全然分かってなかったので苦労しました。

1000ミリ秒経過したら1秒増やして0ミリ秒に戻して、60秒になったら1分増やして0ミリ秒に戻して、60分になったら〜の処理がどうやったら出来るのかなとググったら似たようなのを作ってる人がいたので時間計算部分を参考させていただきました。

getanwar/vuejs-countdown | GitHub

下記は日付計測処理のコード一覧です。

年数は 0に戻すことが無いので余り計算は不要。

Math.floor(1000 % 1000); // 1000ミリ秒を計測
Math.floor(1000 / 1000) % 60; // 60秒を計測
Math.floor(60000 / 1000 / 60) % 60; // 60分を計測
Math.floor(3600000 / 1000 / 60 / 60) % 24; // 24時間を計測
Math.floor(86400000 / 1000 / 60 / 60 / 24) % 365; // 365日を計測
Math.floor(31536000000 / 1000 / 60 / 60 / 24 / 365); // 年

一時停止させた後のに計測を再開させる処理

これも結局は自分が時間の計算方法、タイマー処理が全然分かってなくて、どうやって実装するのかなと悩んだ部分です。

以下、コードの抜粋です。

javascript

// 一部抜粋
new Vue({
  data: {
       // requestAnimationFrame(cb) の返り値(requestID)が入る
    animateFrame: 0,
    // 現在時刻
    nowTime: 0,
    // 現在時刻 と スタートボタンを押した時刻 の差
    diffTime: 0,
    // スタートボタンを押した時刻
    startTime: 0,
    // タイマーが走っているか判定
    isRunning: false
  },
  methods: {
    // 現在時刻から引数に渡した数値を startTime に代入
    setSubtractStartTime: function (time) {
      var time = typeof time !== 'undefined' ? time : 0;
      // performance.now() が返すタイムスタンプは Date.now() よりも精度が高い
      this.startTime = Math.floor(performance.now() - time);
    },
    // タイマーをスタートさせる
    startTimer: function () {
      // loop()内で this の値が変更されるので退避
      var vm = this;
      vm.isRunning = true;
      vm.setSubtractStartTime(vm.diffTime);
      // ループ処理
      (function loop(){
        vm.nowTime = Math.floor(performance.now());
        vm.diffTime = vm.nowTime - vm.startTime;
        vm.animateFrame = requestAnimationFrame(loop);
      }());
    },
    // タイマーを停止させる
    stopTimer: function () {
      this.isRunning = false;
      cancelAnimationFrame(this.animateFrame);
    }
  }
  // 以下省略...
});

startTimer() ではスタートボタンを押下した時刻と現在時刻の差分(diffTime )を代入するメソッド setSubtractStartTime() を発火させて現在時刻(startTime)を代入します。

続いてループ処理です。

stopTimer() を発火させるまで下記1〜3の処理が繰り返されます。

  1. 関数宣言した即時関数内で、現在時刻(nowTime)に performance.now() を代入。
  2. 現在時刻の差分(diffTime)に、現在時刻(nowTime)と、スタートボタンを押下した時刻(startTime)の差を代入。
  3. requestAnimationFrame() の引数に即時関数である loop() を渡して処理を繰り返します。
    requestAnimationFrame() 返り値である requestID は、引数に渡した即時関数を停止させる為に animateFrame に代入します。

stopTimer() を発火させると、 cancelAnimationFrame()animateFrame に代入されている requestID のフレームアニメーションのリクエストをキャンセル(一時停止)します。

再び startTimer() を発火させると一時停止していた時刻から処理が再生されます。

computed(算出プロパティ)

datahours, minutes などの時間の値を持たせたいプロパティの場合、view にロジックを書く必要が出てくる為、とある値をロジックを介して表示させる場合は computed で定義させます。

computed 内で定義したプロパティは data と同じように Vueインスタンス内で使用出来ます。
※基本的には getter関数のみ使用できます。setter関数を使用したい場合はそれぞれ定義する必要があります。

pushTime() 関数内は、computed の値をオブジェクトへそれぞれ代入し、ラップを保存する times 配列に追加しています。

javascript

new Vue({
  // 一部抜粋
  methods: {
    // 計測中の時間を配列に追加
    pushTime: function () {
      this.times.push({
        // computed で定義した hoursの値
        hours: this.hours,
        // computed で定義した minutesの値
        minutes: this.minutes,
        // computed で定義した secondsの値
        seconds: this.seconds,
        // computed で定義した milliSecondsの値
        milliSeconds: this.milliSeconds
      });
    }
  },
  computed: {
    // 時間を計算
    hours: function () {
      return Math.floor(this.diffTime / 1000 / 60 / 60);
    },
    // 分数を計算 (60分になったら0分に戻る)
    minutes: function () {
      return Math.floor(this.diffTime / 1000 / 60) % 60;
    },
    // 秒数を計算 (60秒になったら0秒に戻る)
    seconds: function () {
      return Math.floor(this.diffTime / 1000) % 60;
    },
    // ミリ数を計算 (1000ミリ秒になったら0ミリ秒に戻る)
    milliSeconds: function () {
      return Math.floor(this.diffTime % 1000);
    }
  }
});

filters(フィルター)

時刻をゼロ埋めするのに使いました。

第1引数の value には view に記載してある {{ hours }}{{ minutes }} の値が渡されます。

第2引数には 0埋めしたい桁数を数値で渡します。

引数を指定しない場合は 2桁で 0埋めします。

javascript

filters: {
  // ゼロ埋めフィルタ 引数に桁数を入力する
  // ※ String.prototype.padStart() は IEじゃ使えない
  zeroPad: function(value, num){
    var num = typeof num !== 'undefined' ? num : 2;
    return value.toString().padStart(num,"0");
  }
}

html

<p>Elapsed Time:
  {{ hours }} :
  {{ minutes | zeroPad }} :
  {{ seconds | zeroPad }} :
  {{ milliSeconds | zeroPad(3) }}</p>

最終的なコード

GitHubにプッシュしたので見てあげて下さい。

参考URL

おわり

jQuery でDOM操作は結構やってきてたつもりですが、自分の知らない部分が改めて認識できました。

そろそろ vuex とか vue-router あたりを覚えたい…。

SPONSOR

この記事をシェアする

コメント

関連記事