1
0

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 1 year has passed since last update.

すでに作ったゲームサイトを、実際にJQueryからVueにしてみた

Posted at

以前、こちらのサイトでカードを利用したボードゲームをオンラインで遊べるようにしてみた。

そして、ここでは以前作ったJQueryのオンラインのボードゲームをVueにしてみたらこうなる。
と言うイメージを書いてみました。

その後

まずはVueの勉強

JQueryをVueかにしようと思い、オンラインサイトを色々見てみた、、、

結果的に、良く分からなかった。やはりホントは違い必要最小限しか書いていないため、Vue.jsやらVUE CLIとか良く分からなかったので、Amazonで電子書籍を買った

この本は中々良かったです。内容としては、最初にVue.jsをCDNを使う方法。
簡単なVueならこれで問題なし。
そして、Vue cliを使った本格的なプロジェクト構築。
その後にNuxt.jsを使ったページ管理ができるフレームワークの構築。
最後にはFirebaseとの接続など書いてった。

けど、今回はVue cliのプロジェクトまでわかれば良かったので、そこまで読んでからあとは実践。

その結果

まずはこちらがJQueryで頑張ったもの

AsIs

index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>THE GAME(ザ・ゲーム) ONLINE</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
    <script src="{{ url_for('static', filename='js/script.js') }}"></script>
  </head>
  <body>
    このゲームは1~5人用となっています。<br/>
    <div id='app2'>
      <div id='entry' style='display:none'>
        <input id="cName_inp" placeholder="ニックネームを入力してください"><br/>
        <br/>
        {% if gameid %}
        {% else %}
        <button id='createGame'>ゲームを作る(Make a Game)</button><br/>
        Game ID: <span id='gId'></span><br/>
        <input type='text' id='uriWgId' /><input type='button' id='clickCopy' value='copy'><br/>
        {% endif %}
        Game Status: <span id='gStatus'></span><br/>
        <hr/>
        <button id="joinGame">ゲームに参加する(Join the Game)</button><br/>
        {% if gameid %}
        <input type='hidden' id="gId_inp" value='{{ gameid }}'><br/>
        {% else %}
        <input id="gId_inp" placeholder="GameIDを入力してください"><br/>
        {% endif %}
        Your ID: <span id='cId'></span><br/>
        Your Name: <span id='cName'></span><br/>
        <hr/>
        <h2>Member Applying</h2>
        <span id='applyingList'></span>
        <br/>
      </div>
      <div id='sec1' style='display:none'>
        <hr/>
        <button id="startGame">ゲームを始める(Let's start the Game)</button><br/>
        <input type='checkbox' name='rule_type' id="rule_type" value='original'>オリジナルルール(Use Original Rule)<br/>
        1.&nbsp;直前がぞろ目ならぞろ目を出せる(大小制限なし)<br/>
        2.&nbsp;10単位での戻りが無制限(大小制限なし)<br/>
      </div>
      <div id='sec2' style='display:none'>
        <h2>Member Rotation</h2>
        <span id='routeList'></span>
        <hr/>
        Your Turn: <span id='yTurn'></span><br/>
        Your Cards:<br/>
        <span id='holdCards'></span><br/>
        <div id='sec3' style='display:none'>
          <input type='number' min='2' max='99' id="cardNum_inp" placeholder="番号を入力してください" style="width:200px;"><br/>
          <button id="putCard">カードを出す(Put the cards)</button><br/>
          <br/>
          <button id="nextPlayer">次の人へ(Go on Next user)</button><br/>
        </div>
        <hr/>
        Game Cards:<br/>
        <input type='radio' name='area' id='area' value='0'/>[100&nbsp;>&nbsp;2]&nbsp;<span id='gameCards0'></span><br/>
        <input type='radio' name='area' id='area' value='1'/>[100&nbsp;>&nbsp;2]&nbsp;<span id='gameCards1'></span><br/>
        <input type='radio' name='area' id='area' value='2'/>[1&nbsp;>&nbsp;99]&nbsp;<span id='gameCards2'></span><br/>
        <input type='radio' name='area' id='area' value='3'/>[1&nbsp;>&nbsp;99]&nbsp;<span id='gameCards3'></span><br/>
        Submited Cards in turn:<br/>
        <div id='submitCards'></div>
        <hr/>
      </div>
      <br/>
      <span id='message'></span><br/>
    </div>
  </body>
</html>
script.js
var timeout = 1000;
var timer = '';

$(function() {
  var gId = '';
  var cId = '';
  $('#entry').show();

  $('#clickCopy').click(function(){
    var text = $('#uriWgId').val();
    var clipboard = $('<textarea></textarea>');
    clipboard.text(text);
    $('body').append(clipboard);
    clipboard.select();
    document.execCommand('copy');
    clipboard.remove();
  });

  // Create Game
  $('#createGame').click(function() {
    $('#message').empty();
    $.ajax('/create' + '/' + $('#cName_inp').val(),
      {
        type: 'get',
      }
    )
    .done(function(data) {
      $('#gId').text(data);
      $('#cId').text(data);
      $('#cName').text($('#cName_inp').val());
      $('#gStatus').text('waiting');
      $('#uriWgId').val(location.href + data + '/join');
      gId = data;
      cId = data;
      $('#sec1').show();
      timer = setTimeout(status_check(gId, cId), timeout)
    })
    .fail(function() {
      $('#message').text('エラーが発生しました');
    });
  });

  // Join Game
  $('#joinGame').click(function() {
    $('#message').empty();
    $.ajax('/' + $('#gId_inp').val() + '/join/' + $('#cName_inp').val(),
      {
        type: 'get',
      }
    )
    .done(function(data) {
      _tmp = data.split(' ,');
      $('#cId').text(_tmp[0]);
      $('#cName').text(_tmp[1]);
      $('#gStatus').text(_tmp[2]);
      gId = $('#gId_inp').val();
      cId = _tmp[0];
      timer = setTimeout(status_check(gId, cId), timeout)
    })
    .fail(function() {
      $('#message').text('エラーが発生しました');
    });
  });

  // Start Game
  $('#startGame').click(function() {
    $('#message').empty();
    $.getJSON('/' + gId + '/start/' + $('input:checkbox[name="rule_type"]:checked').val(),
      {
        type: 'get',
      }
    )
    .done(function(data) {
    })
    .fail(function() {
      $('#message').text('エラーが発生しました');
    });
  });

  // Put your card
  $('#putCard').click(function() {
    $('#message').empty();
    // put your card
    $.ajax('/' + gId + '/' + cId + '/set/' + $('input[name="area"]:checked').val() + '/' + $('#cardNum_inp').val(),
      {
        type: 'get',
      }
    )
    .done(function(data) {
      $('#message').text(data);
    })
    .fail(function() {
      $('#message').text('エラーが発生しました');
    });
    $('#cardNum_inp').val('')
  });

  // Next player
  $('#nextPlayer').click(function() {
    $('#message').empty();
    $.ajax('/' + gId + '/next',
      {
        type: 'get',
      }
    )
    .done(function(data) {
      // console.log(data)
      $('#message').text('次の人に移動しました');
    })
    .fail(function() {
      $('#message').text('エラーが発生しました');
    });
  });
});

var status_check = function(gId, cId){
  setTimeout(function(){
    $('#message').empty();
    // all status
    $.getJSON('/' + gId + '/status',
      {
        type: 'get',
      }
    )
    .done(function(data) {
      console.log(data)
      $('#gStatus').text(data.status);
      playerPos = 0;

      // Applying List
      $('#applyingList').empty();
      for(var pIdx in data.players){
        // console.log(data.players[pIdx])
        $('#applyingList').append(data.players[pIdx].nickname + '(' + data.players[pIdx].playerid + ')' + ',');
        if(cId == data.players[pIdx].playerid){
          playerPos = pIdx;
        }
      }

      if(data.status == 'started'){
        $('#entry').hide()
        $('#sec2').show()
        // route List
        $('#routeList').empty();
        for(var rIdx in data.routelist){
          if(data.routelist[rIdx].playerid == data.routelist[data.routeidx].playerid){
            $('#routeList').append('<b>&gt;' + data.routelist[rIdx].nickname + '</b><br/>');
          }else{
            $('#routeList').append(data.routelist[rIdx].nickname + '<br/>');
          }
        }

        // game cards
        $('#gameCards0').text(data.hightolow[0])
        $('#gameCards1').text(data.hightolow[1]);
        $('#gameCards2').text(data.lowtohigh[0]);
        $('#gameCards3').text(data.lowtohigh[1]);

        // hold cards
        $('#holdCards').text(data.players[playerPos].holdcards.join(','))
        // submit cards
        $('#submitCards').text(data.submit.join(','))

        // checking turn
        if(data.routelist[data.routeidx].playerid == cId){
          $('#yTurn').text('your turn')
          $('#sec3').show();
          if(data.submit.length >= 2){
            $('#nextPlayer').prop('disabled', false);
          }else{
            $('#nextPlayer').prop('disabled', true);
          }
        }else{
          $('#yTurn').text('not your turn')
          $('#sec3').css('display', 'none');
        }
      }
    })
    .fail(function() {
      $('#message').text('エラーが発生しました');
    });
    timer = setTimeout(status_check(gId, cId), timeout)
  }, timeout);
}

ToBe

App.vue
<template>
  <v-container>
    <v-flex xs10>
    <v-card-title class="font-weight-bold">THE GAME</v-card-title>
    <v-card-text>このゲームは1~5人用となっています。</v-card-text>
      <br/>
    <div v-if="game == '***'">
      <v-text-field v-model="cName_inp" label="ニックネームを入力してください" filled></v-text-field><br/>
      <div v-if="!game_id">
        <v-btn v-on:click='createGame'>ゲームを作る(Make a Game)</v-btn><br/>
      </div>
      <div v-else>
        <v-btn v-on:click="joinGame">ゲームに参加する(Join the Game)</v-btn><br/>
      </div>
    </div>
    <div v-else>
      <!-- {{ game }} -->
      <div v-if="game.status == 'waiting'">
        Your ID: {{ client_id }}<br/>
        Your Name: {{ nick_name }}<br/>
        <!-- clint:{{ client }} -->
        <div v-if="!guest">
          <v-row>
            <v-col cols='12' sm='6'>
              <v-text-field v-model='uriWgId' readonly></v-text-field>
            </v-col>
            <v-col cols='12' sm='6'>
              <v-btn @click='clickCopy'>copy</v-btn>
            </v-col>
          </v-row>
          <br/>
          <v-btn v-on:click="startGame">ゲームを始める(Let's start the Game)</v-btn><br/>
          <v-checkbox v-model='rule_type'>
            <template v-slot:label>
              オリジナルルール(Use Original Rule)<br/>
              1.&nbsp;直前がぞろ目ならぞろ目を出せる(大小制限なし)<br/>
              2.&nbsp;10単位での戻りが無制限(大小制限なし)<br/>
            </template>
          </v-checkbox>
        </div>
      </div>
      <div v-else-if="game.status == 'started'">
        Your ID: {{ client_id }}<br/>
        Your Name: {{ nick_name }}<br/>
        Last cards: {{ game.stocks.length }}<br/>
        <br/>
        Your Turn: {{ (turn? 'Your turn':'Not your turn') }}
        <br/>
        Your Cards:<br/>
        <table>
          <tr>
            <td v-for="(value, key) in client.holdcards" :key="key">
              <ul>
                <template v-if='turn'>
                  <li v-on:click='putCard(value)' class='card'>
                    {{ value }}
                  </li>&nbsp;
                </template>
                <template v-else>
                  <li class='card'>
                    {{ value }}
                  </li>&nbsp;
                </template>
              </ul>
              <!-- <template v-if='turn'>
                <span v-on:click='putCard(value)' class='card'>
                  {{ value }}
                </span>&nbsp;
              </template>
              <template v-else>
                <span class='card'>
                  {{ value }}
                </span>&nbsp;
              </template> -->
            </td>
          </tr>
        </table>
        <br/>
        <br/>
        <template v-if="turn">
          <v-btn v-on:click="nextPlayer">次の人へ(Go on Next user)</v-btn><br/>
        </template>
      </div>
      <hr/>
      <br/>
      <h2>Member Applying</h2>
      <span v-for="(player, key) in game.players" :key="key">
        {{ player.nickname }}({{ player.playerid }})&nbsp;
      </span>
      <br/>
      <h2>Game Cards</h2>
      下から配置したい場所をエリアを選択してください<br/>
      <table class='board'>
        <tr v-for="(hightolow, key) in game.hightolow" :key="key" class='board'>
          <td v-on:click='setArea(key)' class='board'>
            <template v-if="key === sel_area"></template>
            <ul>
              <li v-for="(card, key) in hightolow" :key="key">
                <span class='card'>
                  {{ card }}
                </span>&nbsp;
              </li>
            </ul>
          </td>
        </tr>
      </table>
      <table>
        <tr v-for="(lowtohigh, key) in game.lowtohigh" :key="key" class='board' >
          <td v-on:click='setArea(key+2)' class='board'>
            <template v-if="key+2 === sel_area"></template>
            <ul>
              <li v-for="(card, key) in lowtohigh" :key="key">
                <span class='card'>
                  {{ card }}
                </span>&nbsp;
              </li>
            </ul>
          </td>
        </tr>
      </table>
    </div>
    {{ error_message }}
    </v-flex>
  </v-container>
</template>

<script>
const axios = require('axios');

export default {
  data: function(){
    return {
      cName_inp: '',
      game: '***',
      error_message: '',
      guest: false,
      turn: false,
      rule_type: false,
      game_id: '',
      uriWgId: 'aaaa',
      area: '',
      sel_area: '',
    }
  },
  mounted() {
    this.game_id = this.$route.query.game_id;
    this.guest = (this.$route.query.game_id != ''? true : false);
    console.log(process.env.NODE_ENV);
  },
  methods: {
    clickCopy: function() {
      navigator.clipboard.writeText(this.uriWgId)
      .then(() => {
          // console.log("copied!");
          alert('copied!');
      })
      .catch(e => {
          console.error(e);
      })
    },
    createGame: function() {
      axios.get(process.env.VUE_APP_API_BASE_URL+'create/'+this.cName_inp).then((res) => {
        this.game_id = res.data;
        this.client_id = this.game_id;
        this.guest = false;
        // this.uriWgId = 'http://localhost:5000/?game_id='+this.game_id;
        this.uriWgId = window.location.href+'?game_id='+this.game_id;
        console.log(res.data);
        setInterval(() => {
          this.status_check();
        }, 1000);
      })
    },
    joinGame: function() {
      axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/join/'+this.cName_inp).then((res) => {
        console.log(res.data);
        this.client_id = res.data.split(',')[0].trim();
        this.guest = true;
        setInterval(() => {
          this.status_check();
        }, 1000);
      })
    },
    startGame: function() {
      axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/start'+(this.rule_type?'/original':'')).then((res) => {
        this.game = res.data;
        // console.log(this.game);
      })
    },
    nextPlayer: function() {
      axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/next');
      this.error_message = '';
    },
    putCard: function(value) {
      if(this.sel_area === ''){
        alert('下から配置したいエリアを選択してください');
        return;
      }
      axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/'+this.client_id+'/set/'+this.sel_area+'/'+value)
      .then(res => {
        this.error_message = res.data;
      })
    },
    setArea: function(value) {
      this.sel_area = value;
    },
    status_check: function() {
      axios.get(process.env.VUE_APP_API_BASE_URL+this.game_id+'/status').then((res) => {
        this.game = res.data;
        // console.log(this.game);

        this.client = res.data.players.find((v) => v.playerid.trim() == this.client_id.trim());
        this.nick_name = this.client.nickname;
        if(this.game.status == 'started'){
          this.turn = (this.game.routelist[this.game.routeidx].playerid == this.client_id? true: false);
        }
      })
    },
  },
}
</script>

Vue.jsでも少しゴミゴミした書き方になってしまいましたが、JQueryとVueの違いを理解してもらうには十分かと思います。

結果的にわかったこと

  • コードをhtml/jsで分けて考えず、htmlを1つのjsと考えながらプラグラミングできる便利さ
  • データの反映について、いちいちjs側で出力ロジックを記載しなくても、必要な変数を更新すれば勝手に反映される

なんか、htmlへ表示する際に、わざわざ出力ロジックを書く必要がないのが大変便利。

ということで、今後はVue.jsをメインに使っていこうと思います。
また、Nuxt.jsや、Firebaseなどのリアルタイムデータベースを使ったものにバージョンアップしていきたいと思います。

なお、Vueで書き直した「THE GAME」はこちらのリンクから遊べます。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?