Vue.jsでそれっぽい何かを作る

株式会社イメージ・マジックの技術ブログ、先週の担当のsoenoです。 何か面白いものを…と考えること数日。いつのまにか期限は過ぎていました。 そこで今回は終わらせることを大切に美しいコード的なものからは目をそらしつつvue.jsを触ろうと思います。


作るもの

左のマス目を見ながら右に同じ図形を写し取るというゲーム(の手前)のようなものを作成しました。 落下やパーツの消去がないので、テトリスや、インベーダゲームよりとりかかりやすいかと思います。 ここから発展させて爆弾を埋め込んで危険度を表示させるとマインスイーパー、数字を隠せば数独、文字を入れてもらうとクロスワードなりになるのではないでしょうか。

使うもの

  • CDN: Vue(ダウンロードすらしたくないとき)
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
  • HTML
  • css

進めかた

見本の絵を表示させる。

  1. htmlでvueのelに割り当てる要素を作成します。
  2. スクリプト内に多次元配列を作成。trueを選択状態として実際お値で埋める。
  3. html内でv-forを使用して配列分の要素を作成。
  4. cssで1枡分のスタイルを設定。(まず幅と高さ。)
  5. 多次元配列から1枡分の選択状態を表示させる。

操作側を作る

  1. 見本同様に多次元配列を作成。(お好みでスクリプトから生成)
  2. 見本同様にv-forを使用して配列分の要素を作成。
  3. 見本用のcssをこちらにも充てる。
  4. 見本同様に多次元配列から1枡分の選択状態を表示させる。
  5. クリックできるようにする。

イメージの確認

  1. マス目の選択状態が変わったら見本と比較して一致しているかを確認する。

結果

以下のコードに~.html的な名前を付けてデスクトップに置くと動くはずです。


よろしければ落として遊んでみてください。
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>vue</title>
    <style type="text/css">
      .wrapStyle{
          margin: 0 50px;
          display: inline-block;
      }
      .celWrap{
          display: inline-block;
          box-sizing: content-box;
          margin:0;
          display: inline-block;
      }
      .cel{
          border:solid thin #eeeeee; 
          box-sizing: border-box;
          display: inline-block;
          margin:0;
          padding:0;
      }
      .cel.is-active{
          background-color:#111111;
      }
    </style>
</head>
<body>
<div id="app">
  <div class="wrapStyle" v-bind:style="boxStyle">
    <p>見本</p>
    <div v-for="(items, yoko) in resultList" v-bind:style="celWrap">
      <div v-for="(item, tate) in items" 
            v-bind:style="celStyle" class="cel" 
            v-bind:class="{'is-active': resultList[yoko][tate]}">
      </div> 
    </div>
  </div>
  <div class="wrapStyle" v-bind:style="boxStyle">
    <p>操作エリア</p>
    <div v-for="(items, yoko) in userList" v-bind:style="celWrap">
      <div v-for="(item, tate) in items"
           v-on:click="changeCel(userList[yoko][tate])" :data-yoko="yoko" :data-tate="tate"
           v-bind:style="celStyle" class="cel"
           v-bind:class="{'is-active': userList[yoko][tate]}">
      </div>
    </div>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script language="javascript">
new Vue({
  el: '#app',
  data: {
    cellSize:30,
    resultList: [
      [false,false,true,false,false],
      [false,false,true,false,false],
      [false,false,true,false,false],
      [false,false,true,false,false],
      [false,false,true,false,false],
    ],
    userList: [
      [false,false,false,false,false],
      [false,false,false,false,false],
      [false,false,false,false,false],
      [false,false,false,false,false],
      [false,false,false,false,false],
    ],
    
    boxStyle:{
      width:0,
    },
    celWrap:{
      height:0,
    },
    celStyle:{
      width:0,
      height:0,
    },
  },
  created() {
    this.celStyle.width = this.cellSize+ 'px';
    this.celStyle.height = this.cellSize+ 'px';
    this.celWrap.height = this.cellSize+ 'px';
    this.boxStyle.width = this.cellSize * this.resultList[0].length+ 'px';
  },
  
  methods: {
    changeCel() {
      const yoko = parseInt(event.currentTarget.dataset.yoko);
      const tate = parseInt(event.currentTarget.dataset.tate);
      Vue.set(this.userList[yoko], tate, !this.userList[yoko][tate]);

      //表示が変更されたら揃ったか確認する。
      this.check();
    },
    check() {
      for(let i=0,len = this.resultList.length; i<len; i++){
        for(let ii=0,len2 = this.resultList[i].length; ii<len2; ii++){
          if(this.userList[i][ii] !== this.resultList[i][ii]){
            //不一致があったら処理を抜ける
            return;
          }
        }
      }
      setTimeout(function () {
        alert("揃いました。");
      }, 100);
    },
  },
})
</script>
</body>
</html>
 

改善、変更候補

  1. cellSizeの値を変更すると画面上の1枡分のサイズが変わります。簡素な処理がいい場合はここを確定させcssに持たせると処理は短くなります。
  2. userListにユーザーの選択を持たせていますが、ここは結果用の配列から生成したほうがミスによる数の不一致などがなくなりいいのではないかと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です