こんにちは、CTOの奥田です。
さて今回はVueを使ったカウントアップ機能を実装してみたいと思います。
Promiseやasync/awaitの勉強にもなると思いますのでぜひご参考にしてみてください。
こちらが今回のデモです。
Table of contents
必要なリソースを読み込む
まずは必要なリソースを読み込みます。
最近Vue3がリリースされましたが、今回はVue2を使います。
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
Viewを構築する
次にViewを構築します。
今回は最終カウント数とカウント間隔を入力できるようにし、一時停止/再開ができる機能もつけてみたいと思います。
<div id="app"> <div class="container"> <div class="c-counter"> <div class="c-counter__value"> {{ value | format }} </div> <div class="c-counter__input--group mb-3"> <label class="c-counter__input--text" for="duration">Duration / ms</label> <input type="number" id="duration" class="form-control c-counter__input"> </div> <div class="c-counter__input--group mb-4"> <label class="c-counter__input--text" for="end">End</label> <input type="number" id="end" class="form-control c-counter__input"> </div> <button type="button" class="btn btn-default c-counter__pause"><span>Pause</span></button> <button type="button" class="btn btn-primary c-counter__start">Start</button> </div> </div> </div>
Vueインスタンスを生成します。
elには#appを、dataにはそれぞれ初期値を入れておきます。
new Vue({ el: '#app', data: { is_running : false, value: 0, to: 1000, pause : false, timeout: 10 }, methods: { } });
数値を3桁ずつカンマ区切りにするためにフィルターを追加しておきます。
Vue.filter('format', function (value) { return value.toString().replace(/(\d)(?=(\d{3})+$)/g, '$1,'); })
カウンターを実装する
カウンターを実装していきます。
カウント間隔用のinputにv-modelを設定し、値を変更可能にします。
<input type="number" id="duration" class="form-control c-counter__input" v-model="timeout">
同様に最終カウントの設定を変更可能にします。
<input type="number" id="end" class="form-control c-counter__input" v-model="to">
Startボタンに@clickで_startメソッドをバインドします。
v-bind:disabledを設定し、is_runningがtrueの時はボタンが押せないようにします。
<button type="button" @click="_start" class="btn btn-primary c-counter__start" v-bind:disabled="is_running">Start</button>
カウンターメソッドを実装していきます。
methods: { wait (){ return new Promise(resolve => setTimeout(resolve, this.timeout)); }, async _count(){ if(this.to == this.value) { this.is_running = false return false } if(this.timeout > 0){ await this.wait() } this.is_running = true if(this.to > this.value){ this.value ++ }else{ this.value -- } this._count() }, _start(){ if(this.is_running) return false this.pause = false; this._count() }, }
Startボタンを押した際にthis._count()メソッドを実行します。
asyncを宣言することで内部のメソッドにawaitを宣言できます。
awaitを宣言したメソッド内でPromiseを返すことで処理の終了を待ってから次の処理に移行することができます。
wait()の中でsetTimeoutを実行し、一定時間処理を停止させています。
その後、カウント数と最終カウント数に応じて値を足し引きし、カウント数と最終カウント数が同じになるまで再帰的にthis._count()メソッドを呼び出しています。
Pause/Resumeを実装する
次に一時停止/再開機能を実装します。
まず、Resumeボタンに@click=”_pause”で_pauseメソッドをバインドします。
そして、ボタンのテキストをspanで囲い、v-if=”pause”とv-if=”!pause”で表示を切り替えます。
<button type="button" @click="_pause" class="btn btn-default c-counter__pause"><span v-if="!pause">Pause</span><span v-if="pause">Resume</span></button>
_pauseメソッドは簡単です。this.pauseをtoggleし、_count内でthis.pauseがtrueならreturn falseをするだけです。
_pause(){ this.pause = !this.pause; this._count() }
async _count(){ if(this.pause) { this.is_running = false return false } ...
これでカウンターの一時停止/再開が実装できました。
さいごに
今回は簡単にカウンター機能の実装をご紹介してみました。
Vueは非常に少ないコードで実装できるのでサンプルとしても見やすいと思います。
何よりわかりにくいPromiseやasync/awaitの処理について、少しでもお役立ていただけましたら幸いです。