Vue.js v2でリアクションタイマーを作成する

Vue.js

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を使うには、次の手順の通りです。

  1. 1importする
  2. 2componentsにBlockを加える
  3. 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>

Vue.js

Posted by devsakaso