3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vue.js初心者が学習のためにシンプルなマインスイパーを作った

Last updated at Posted at 2019-12-04

はじめに

この記事は 2019 年まで素の JavaScript でコーディングしていた人間が初めて Vue.js を学習したときの記録です:notepad_spiral:
↓のURLにアクセスするとアプリに触れます。
https://vue-mine.firebaseapp.com

何を作ったか?

こんな感じのアプリです。
vue-mine.gif

見た目はあれです。今回は簡単な仕上がりを目指しました:thumbsup:(適当)
データバインディングの動きの確認をする程度の目的ですので、セルは単純な色分けで区別しています。

開発環境

Windows10
Node.js v12.13.1
Vue.js 2.6.10
vue/cli 4.1.1
Atom 1.41.0
Github https://github.com/t-ube/vue-mine

作成時につまづいた点と答え

Q. まずどのファイルをいじればいいのかわからない

A. <プロジェクトフォルダ>\src\App.vue を編集する

よっしゃさっそく作り始めたるわーと思い、コマンドラインで

vue create <プロジェクト名>

と打ち込んだ直後に僕は手が止まりましたが、 @567000 様の記事を参考にして、まずは App.vue 内の <template> を色々といじるところから始めました。
[Vue.js を vue-cli を使ってシンプルにはじめてみる][1]
[1]:https://qiita.com/567000/items/dde495d6a8ad1c25fa43

Q. 表示条件の分岐ってどうやる?

A. 表示する要素のタグ内に制御用のif文を書く

Vue.js ではタグ内の v-if ディレクティブに指定した値が評価されて表示が切り替わります。
:point_down:のコードを例にとります。

App.vue
<p v-if="gameover">Game Over</p>
<p v-else-if="complete">Clear</p>
<p v-else>Please click green cell</p>

まず、変数 gameover の値が真のときに Game Over が表示されます。
それ以外でかつ変数 complete が真のときに Clear が表示されます。
それ以外のときには Please click green cell が表示されます。

素の JavaScript で getElementById で要素を取得してから表示/非表示の切り替えとか面倒なことをしていたのが嘘のようです。Vue.js は素晴らしいです:relaxed:

Q. マインスイーパーのセルの並べ方ってどうやる?

A. v-for で要素を並べて grid-template を使う

Vue.js / CSS の合わせ技です。

App.vue
<template>
~略~
<div id="map">
      <div v-for="cell in grid_size*grid_size"

</template>

<script>

#map {
  --grid-size: 6;
  display: grid;
  grid-template-columns: repeat(var(--grid-size), 30px);
  grid-template-rows: repeat(var(--grid-size), 30px);
  justify-content: center;
  align-content: end;
  padding: auto;
  margin: auto;
}

Vue.js では要素のタグ内に v-for ディレクティブを付けることで、同じ要素を繰り返し配置できます。

@miyauchoi 様の記事を参考にさせていただきました。
[200行のVue.jsでスネークゲームを作った][2]
[2]:https://qiita.com/miyauchoi/items/f753e5fa0209ab2034dd

Q. for 文で要素配置したらエラーになるんだけど?

A. v-bind:key が必要

v-for ディレクティブで要素を連続配置したらコンパイルエラーです。本当にありがとうございました。

./src/App.vue
Module Error (from ./node_modules/eslint-loader/index.js):
error: Elements in iteration expect to have 'v-bind:key' directives (vue/require-v-for-key) at src\App.vue:8:7:
   6 |     <p v-else>Please click green cell</p>
   7 |     <div id="map">
>  8 |       <div v-for="cell in grid_size*grid_size"
     |       ^
   9 |        v-on:click="dig_cell(cell-1)"
  10 |        :class="{
  11 |          cell: true,

理由はエラーメッセージに書かれているので一目瞭然ですね!
僕は軽く1時間は悩みましたがね。
:point_down:のようにコードを直しました。

App.vue
<div v-for="cell in grid_size*grid_size"
       v-on:click="dig_cell(cell-1)"
       v-bind:key="cell.id"
       :class="{

要素に値をバインドさせるためにidが必須とのことなので、v-for ディレクティブが動作しない怪現象に悩んだら v-bind:key を指定しているかを確認しましょう。

Q. クラスってどうやって作るのかわからない

A. コンポーネント化する(多分)

Google 先生で「Vue.js クラス」って検索しても CSS しかでてきません。
データを構造化したいときはどうするのでしょうか:disappointed_relieved::sos:
コンポーネント化するっぽいのですが。
これは諦めて今後の課題としました。
というわけで、セルごとのパラメータ(周囲の地雷の数、地雷フラグ、掘削フラグ)はすべて独立した配列で管理しています。

App.vue
data () {
    return {
~略~
      surround_bomb: [],
      is_bomb: [],
      is_dig: [],
~略~
    }

気持ちが悪いですね。早いところ修正したいと思います。

Q. セル要素の二次元座標はパラメータ化する?

A. 座標パラメータは不要で、配列サイズから計算する

配列のインデックスと座標の変換関数を用意します。
配列のインデックスを得るための関数 get_index に渡した座標が二次元座標の領域を超えている場合、存在しないものとして -1 を返しています。

App.vue
get_cell_x(index){
   return (Math.floor(index%this.grid_size));
},
get_cell_y(index){
   return (Math.floor(index/this.grid_size));
},
get_index(x,y){
   if(x < 0 || y < 0) return -1;
   else if(x>=this.grid_size || y>=this.grid_size) return -1;
   return ((y*this.grid_size)+x);
}

セルの周囲の地雷を数えるための関数 count_surrounding_bombs に配列のインデックスを渡すと内部で座標に変換し、周囲の 8 セル分をチェックします。

App.vue

is_bomb_index(index){
   return (this.is_bomb[index] == 1);
},
is_bomb_cell(x,y){
   return (this.is_bomb_index(this.get_index(x,y)));
},
count_surrounding_bombs(index) {
   let x = this.get_cell_x(index);
   let y = this.get_cell_y(index);
   let counts = 0;
   if(this.is_bomb_cell(x-1,y-1)) counts += 1;
   if(this.is_bomb_cell(x,y-1))   counts += 1;
   if(this.is_bomb_cell(x+1,y-1)) counts += 1;
   if(this.is_bomb_cell(x-1,y))   counts += 1;
   if(this.is_bomb_cell(x+1,y))   counts += 1;
   if(this.is_bomb_cell(x-1,y+1)) counts += 1;
   if(this.is_bomb_cell(x,y+1))   counts += 1;
   if(this.is_bomb_cell(x+1,y+1)) counts += 1;
   return counts;
}

感想

Vue.js のデータバインディングって強力ですね。
ミニゲームを簡単に作れてしまうのがすごいです:relaxed:
次はコンポーネント化にチャレンジしてみます。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?