Vue.jsとDjango-Rest-Frameworkで神経衰弱アプリを作ってみる【その3】~神経衰弱の実装編~

[<< その2](https://qiita.com/Butterthon/items/8d986e16bd6f3e540a7e) | [その4 >>](https://qiita.com/Butterthon/items/535f961aed0b3c3c6d98)

# トランプの画像を用意する。
### スクレイピングで「いらすとや」からトランプの画像を取得
[ここ参照](https://qiita.com/Butterthon/items/25588084a590032ded2e)
なんのこっちゃって人は手作業で54枚ダウンロードするか、vueでコーディングするなりしてください。

# トランプ52枚を表示してみる。（Joker抜き）
```PlayScreen.vue
<template>
  <div>
    <span v-for="(card, index) in cards" :key="index">
      <img
        v-bind:src="card"
        alt=""
        width="110"
        height="135"
      />
    </span>
  </div>
</template>

<script>
export default {
  name: 'TopPage',
  data: function () {
    return {
      cards: [],
    }
  },
  methods: {
    setCardImage: function(type) {
      for (let i = 1; i < 14; i++) {
        let number = ('00' + i).slice(-2)
        this.cards.push(require('@/assets/images/card_' + type + '_' + number + '.png'))
      }
    }
  },
  mounted () {
    this.setCardImage('club');
    this.setCardImage('diamond');
    this.setCardImage('heart');
    this.setCardImage('spade');
  }
}
</script>

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/8973566a-b62c-7be6-a6a1-6207c126e891.png)


### トランプをシャッフルする
```PlayScreen.vue
<template>
  ...
  ..
  .
</template>

<script>
export default {
  ...
  ..
  .
  methods: {
    // 追加
    shuffle: function () {
      for (let i = this.cards.length - 1; i > 0; i--){
        let r = Math.floor(Math.random() * (i + 1));
        let tmp = this.cards[i];
        this.cards[i] = this.cards[r];
        this.cards[r] = tmp;
      }
    },
    ...
    ..
    .
  },
  mounted () {
    this.setCardImage('club');
    this.setCardImage('diamond');
    this.setCardImage('heart');
    this.setCardImage('spade');
    this.shuffle(); // 追加
  }
}
</script>

```

シャッフルされるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/015be20d-13b2-bdcc-cda0-78ec752ebb8a.png)

# カードに表裏のオプションを追加する
```PlayScreen.vue
<template>
  <div>
    <span v-for="(card, index) in cards" :key="index">
      <!-- スタイルバインディングで画像のURLを設定する -->
      <img
        v-bind:src="card.isOpen ? card.cardInfo.front : card.cardInfo.back"
        alt=""
        width="110"
        height="135"
      />
    </span>
  </div>
</template>

<script>
export default {
  ...
  ..
  .
  methods: {
    ...
    ..
    .
    // このメソッドを編集
    setCardImage: function(type) {
      for (let i = 1; i < 14; i++) {
        let number = ("00" + i).slice(-2);
        let card = {
          isOpen: false, // 初期状態は裏で表示されるようにする。
          cardInfo: {
            number: i,
            front: require(`@/assets/images/card_${type}_${number}.png`), // 表
            back: require("@/assets/images/card_back.png") // 裏
          }
        };
        this.cards.push(card);
      }
    }
  },
  mounted () {
    ...
    ..
    .
  }
}
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/247ea8f8-09f3-a78a-508c-536c83aeab08.png)

# 画像クリックでカードを表表示にできるようにする。
```PlayScreen.vue
<template>
  <div>
    <span v-for="(card, index) in cards" :key="index">
      <!-- clickイベントを付与し、追加したメソッドを設定する -->
      <img
        v-bind:src="card.isOpen ? card.cardInfo.front : card.cardInfo.back"
        alt=""
        width="110"
        height="135"
        @click="open(index)"
      />
    </span>
  </div>
</template>

<script>
export default {
  ...
  ..
  .
  methods: {
    ...
    ..
    .
    // このメソッドを追加
    open: function(index) {
      // カードを表向きにする
      this.cards[index].isOpen = true;
    }
  },
  mounted () {
    ...
    ..
    .
  }
}
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/d5664a4e-31b8-2220-d76d-f2553149bb37.png)

# 神経衰弱を実装していく
### 2枚めくったらカードが全て裏向きに戻るようにする
```PlayScreen.vue
<template>
  ...
  ..
  .
</template>

<script>
let openCountByPlayer = 0; // プレイヤーが何枚めくったか

export default {
  ...
  ..
  .
  methods: {
    ...
    ..
    .
    // このメソッドを編集する
    open: function(index) {
      // 2枚以上はめくれないようにする
      if (openCountByPlayer + 1 > 2) return;

      // カードを表向きにする
      this.cards[index].isOpen = true;

      openCountByPlayer++;

      if (openCountByPlayer == 2) {
        // 2枚めくったらカードが裏向きになるようにする
        this.reset();
      }
    },
    // このメソッドを追加
    reset: function() {
      setTimeout(() => {
        this.cards.forEach((card, index) => {
          if (card.isOpen) {
            this.cards[index].isOpen = false;
          }
        });
        openCountByPlayer = 0;
      }, 2000);
    },
  },
  ...
  ..
  .
}
</script>
```

### めくった2枚の数字が一致している場合は表向きになったままにする
```PlayScreen.vue
<template>
  <div>
    <span v-for="(card, index) in cards" :key="index">
      <!-- 既に取得されたカード(isGet != null)は表向き表示になるようにする。 -->
      <img
        v-bind:src="(card.isOpen || card.isGet != null) ? card.cardInfo.front : card.cardInfo.back"
        alt=""
        width="110"
        height="135"
        @click="open(index)"
      />
    </span>
  </div>
</template>

<script>
let openCountByPlayer = 0; // プレイヤーが何枚めくったか
const PLAYER = "player";
const COMPUTER = "computer";

export default {
  ...
  ..
  .
  methods: {
    ...
    ..
    .
    // このメソッドを編集
    setCardImage: function(type) {
      for (let i = 1; i < 14; i++) {
        let number = ("00" + i).slice(-2);
        let card = {
          isOpen: false,
          isGet: null, // isGetプロパティを追加
          cardInfo: {
            number: i,
            front: require(`@/assets/images/card_${type}_${number}.png`),
            back: require("@/assets/images/card_back.png")
          }
        };
        this.cards.push(card);
      }
    },
    // このメソッドを編集する
    open: function(index) {
      // 2枚以上はめくれないようにする
      if (openCountByPlayer + 1 > 2) return;

      // カードを表向きにする
      this.cards[index].isOpen = true;

      openCountByPlayer++;

      if (openCountByPlayer == 2) {
        this.isNumbersMatch();

        // 2枚めくったらカードが裏向きになるようにする
        this.reset();
      }
    },
    // このメソッドを編集
    reset: function() {
      setTimeout(() => {
        // 取得されているカード以外を裏向きに戻す
        this.cards.forEach((card, index) => {
          if (card.isOpen && card.isGet == null) {
            this.cards[index].isOpen = false;
          }
        });
        openCountByPlayer = 0;
      }, 2000);
    },
    isNumbersMatch: function() {
      // 表向きの画像を取得する（既に数字が一致しているカード（isGet != null）は除外）
      let openCards = [];
      this.cards.forEach((card, index) => {
        if (card.isOpen && card.isGet == null) {
          openCards.push({ index, card });
        }
      });

      // 数字が一致している場合は取得
      let firstCard = openCards[0];
      let secondCard = openCards[1];
      if (firstCard.card.cardInfo.number == secondCard.card.cardInfo.number) {
        this.cards[firstCard.index].isGet = PLAYER;
        this.cards[secondCard.index].isGet = PLAYER;
      }
    }
  },
  ...
  ..
  .
}
</script>
```

### 獲得した組数を表示する
``` PlayScreen.vue
<template>
  <div>
    <v-card>
      <!-- 追加した算出プロパティを使用 -->
      <v-row no-gutters>
        <v-col>あなたがゲットした組数：{{ getPlayerPairs }}組</v-col>
      </v-row>

      <v-divider class="mb-4"></v-divider>

      ...
      ..
      .
    </v-card>
  </div>
</template>

<script>
let openCountByPlayer = 0; // プレイヤーが何枚めくったか
const PLAYER = "player";
const COMPUTER = "computer";

export default {
  ...
  ..
  .
  // 算出プロパティを追加
  computed: {
    getPlayerPairs: function() {
      if (!this.cards || this.cards.length === 0) return;

      return (
        this.cards.filter(card => { return card.isGet === PLAYER; }).length / 2
      );
    }
  },
  ...
  ..
  .
}
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/ece81024-7b74-5179-a454-c2d509b77bd1.png)

[<< その2](https://qiita.com/Butterthon/items/8d986e16bd6f3e540a7e) | [その4 >>](https://qiita.com/Butterthon/items/535f961aed0b3c3c6d98)