Vue.js v2でリアクションタイマーを作成する
Vue.jsでリアクションの時間を図れるタイマーを作成します。
Vue.jsでリアクションタイマーを作成する
- 親コンポーネントApp.vueに加えて、
- 子コンポーネントBlock.vue
- 子コンポーネントResults.vue
を作成します。
スタートをクリックすると、ランダムな時間の経過後にタイマーが開始されます。
そして、現れたブロックをクリックするまでの時間を計測します。
その計測結果によって、メッセージを切り替えます。
親コンポーネントApp.vue
<template>
<h1>Reaction Timer</h1>
<!-- v-bind:disabled="isPlaying"はtrueのときdisabledにする -->
<button @click="start" v-bind:disabled="isPlaying">スタート</button>
<Block v-if="isPlaying" v-bind:delay="delay" @end="endGame" />
<Results v-if="showResults" v-bind:score="score" />
</template>
Block.vueを使うには、次の手順の通りです。
- 1importする
- 2componentsにBlockを加える
- 3templateタグでBlockタグを使う
Results.vueも同様にimportしていきます。
<script>
import Block from './components/Block.vue';
import Results from './components/Results.vue';
export default {
name: 'App',
components: { Block, Results },
data() {
return {
isPlaying: false,
delay: null,
score: null, //reactionTimeを受け取りたいので格納する変数を作る
showResults: false
};
},
methods: {
start() {
this.delay = 2000 + Math.random() * 3000;
this.isPlaying = true;
this.showResults = false;
},
endGame(reactionTime) {
this.score = reactionTime;
this.isPlaying = false;
this.showResults = true;
}
}
};
</script>
クリックされたら(start()メソッド)、isPlayingをtrueにして表示、結果は非表示にしたいのでshowResultsをfalseにします。
そして、ランダムの時間を経過させるために、delayにランダムな時間を代入します。
ゲームを終わらせるときは、
Block.vueから$emitの第二引数で渡される値を引数で受け取ります。
reactionTimeをscoreに格納して、isPlayingをfalseに戻して非表示にします。
<style scoped>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #444;
margin-top: 60px;
}
button {
background-color: #31b0c7;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
font-size: 18px;
letter-spacing: 1px;
cursor: pointer;
margin: 10px;
}
button[disabled] {
opacity: 0.2;
cursor: not-allowed;
}
</style>
子コンポーネントBlock.vue
<template>
<div class="block" v-if="showBlock" @click="stopTimer">
クリック
</div>
</template>
<script>
export default {
props: ['delay'], //App.vueから受け取る
data() {
return {
// blockはスタート押したら表示したいのでデフォルトがfalseになるように設定
showBlock: false,
timer: null, //10ずつ増やす
reactionTime: 0 //これを増やしてカウントしていく
};
},
// Lifecycle Diagramの一つのmounted()
// mountedは要するにsetTimeoutの関数が実行されるとき
mounted() {
setTimeout(() => {
this.showBlock = true;
this.startTimer();
}, this.delay);
},
// クリックが現れてからクリックされるまでの時間を測りたい
// timerをstartとstopしたいのでmethodsを作成する
methods: {
startTimer() {
this.timer = setInterval(() => {
this.reactionTime += 10;
console.log(this.timer);
}, 10);
},
stopTimer() {
clearInterval(this.timer);
//App.vue(親)でreactionTimeを受け取りたい
// $emitは第一引数に好きな名前、第二引数に渡したいデータ
this.$emit('end', this.reactionTime);
}
}
};
</script>
親コンポーネントであるApp.vueから受け取りたいdelayをpropsを設定して受け取ります。
setInterval()を使って、10msずつreactionTimeに追加して、その値をtimerに格納します。
タイマーを止めるとき、$emitで親コンポーネントであるApp.vueに渡す名前(今回はend)と渡したい値を設定します。
<style scoped>
.block {
width: 300px;
font-size: 1.6em;
border-radius: 20px;
background-color: #31b0c7;
color: white;
text-align: center;
padding: 100px 0;
margin: 40px auto;
}
</style>
子コンポーネントResults.vue
Results.vueでは、結果の表示を担当させます。
App.vueのscoreを使うためには、
App.vueのResultsタグでv-bind:score="score"としてResultsに渡します。
Results.vueでpropsに追加します。
templateタグで使います。
<template>
<p>Reaction time: {{ score }} ms</p>
<p class="rank">{{ rank }}</p>
</template>
<script>
export default {
props: ['score'],
data() {
return {
rank: null
};
},
mounted() {
if (this.score < 260) {
this.rank = '超一流';
} else if (this.score < 400) {
this.rank = '一流';
} else {
this.rank = '二流';
}
}
};
</script>
<style scoped>
.rank {
font-size: 1.8em;
color: #31b0c7;
font-weight: bold;
}
</style>