Vue.js でシンプルなストップウォッチを作ってみた
投稿日:2017年11月19日
お疲れ様です、ナガです。
別に Vue.js
である必要は無いけど勉強がてら作ってみました。
今回学べたこと
- 時間の計算方法(今更だけど…)
- 一定間隔で繰り返す処理(
window.requestAnimationFrame
とwindow.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); // 年
2
3
4
5
6
一時停止させた後のに計測を再開させる処理
これも結局は自分が時間の計算方法、タイマー処理が全然分かってなくて、どうやって実装するのかなと悩んだ部分です。
以下、コードの抜粋です。
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);
}
}
// 以下省略...
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
startTimer()
ではスタートボタンを押下した時刻と現在時刻の差分(diffTime
)を代入するメソッド setSubtractStartTime()
を発火させて現在時刻(startTime
)を代入します。
続いてループ処理です。
stopTimer()
を発火させるまで下記1〜3の処理が繰り返されます。
- 関数宣言した即時関数内で、現在時刻(
nowTime
)にperformance.now()
を代入。 - 現在時刻の差分(
diffTime
)に、現在時刻(nowTime
)と、スタートボタンを押下した時刻(startTime
)の差を代入。 requestAnimationFrame()
の引数に即時関数であるloop()
を渡して処理を繰り返します。requestAnimationFrame()
返り値であるrequestID
は、引数に渡した即時関数を停止させる為にanimateFrame
に代入します。
stopTimer()
を発火させると、 cancelAnimationFrame()
で animateFrame
に代入されている requestID
のフレームアニメーションのリクエストをキャンセル(一時停止)します。
再び startTimer()
を発火させると一時停止していた時刻から処理が再生されます。
computed(算出プロパティ)
data
に hours
, 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);
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
filters(フィルター)
時刻をゼロ埋めするのに使いました。
第1引数の value
には view
に記載してある や
の値が渡されます。
第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");
}
}
2
3
4
5
6
7
8
html
<p>Elapsed Time:
{{ hours }} :
{{ minutes | zeroPad }} :
{{ seconds | zeroPad }} :
{{ milliSeconds | zeroPad(3) }}</p>
2
3
4
5
最終的なコード
GitHubにプッシュしたので見てあげて下さい。
参考URL
- いまさらrequestAnimationFrameを理解する
- getanwar/vuejs-countdown | GitHub
- 算出プロパティとウォッチャ — Vue.js
- フィルター — Vue.js
- performance.now() - Web APIs | MDN
- window.requestAnimationFrame - Web API インターフェイス | MDN
- window.cancelAnimationFrame - Web API インターフェイス | MDN
- String.prototype.padStart() - JavaScript | MDN
おわり
jQuery
でDOM操作は結構やってきてたつもりですが、自分の知らない部分が改めて認識できました。
そろそろ vuex
とか vue-router
あたりを覚えたい…。