LoginSignup
12
10

More than 3 years have passed since last update.

リアルタイムで更新するランキング画面を”手抜き”実装してみた

Last updated at Posted at 2020-08-14

はじめに

ゲームセンターによく「最後にスコアのランキング表示」が出るゲームがあったと思うのですが、
それをちょっとWeb上で実装したいな〜って思ったので実装してみました

▼ イメージ

ただ、単純にランキング表示するだけだとつまらない(?)ので、
WebSocket通信も使って「外部からスコアデータを受信したらランキングをリアルタイムで更新」も作ってみました

完成イメージ

ranking-websocket.gif
新規データがどれかわかりやすいように無駄に色つけてみたりしてますが、まぁそこはお好みでw
コードは GitHub に上げてます

にしても、画面リロードなしでしかも別のブラウザから画面いじれるってすごい仕組みだなぁ・・

構成図

わりとシンプルです
ランキング表示の画面はVueでサクっと作ってます。ほぼテンプレそのまま
データ送信は開発者ツールのコンソール上でサクッと。サーバー側はNode.jsでサクサクっと

お惣菜や唐揚げが手抜き料理ってなるなら、今回の実装こそまさに手抜き実装

ただこれが言いたかっただけだったり・・・

フロント側の準備

とりあえずvue-cliでファイル群を生成します
vue-cliが入ってない方はまずvue-cliをインストールしてください
インストール方法はググればすぐ出てきます(手抜き)

# vueのテンプレ生成
$ vue create sample
## defaultを選択してできあがるまでちょっと待つ

# フォルダ移動
$ cd sample

こんな感じのディレクトリ構造になってると思います

.
├── README.md
├── babel.config.js
├── node_modules/
├── package.json
├── public/
├── src
│   ├── App.vue ⭐修正
│   ├── assets
│   ├── components/
│   │    └── HelloWorld.vue ⭐修正
│   └── main.js
└── yarn.lock

修正するのは .vueファイルの2つだけ

App.vue
<template>
  <div id="app">
    <HelloWorld/>   ←ここらへんをシンプルに
  </div>
</template>
 ~ 他は変更箇所ないので省略 ~

デフォルトだといらない要素があるので削除します

HelloWorld.vue の修正

今回のメインファイルなのでそこそこ手を加えます。全体は GitHub で確認してください!
HTML、JS、CSSごとに説明します!

HTML: ランキングのテーブル表示

ランキング自体は <table></table> で作ってます
要素はデータ追加に合わせてどんどん増えていくので、 <tr></tr>v-for でスコアデータ分ループさせるようにしてます

あとで、このスコアデータの配列にデータを追加する仕組みを作るので、
結果的に「データを追加する → スコアデータの配列が増える → v-for でループする数が増える → 行が増える」となります
ここらへんの動的処理がVueだとめっちゃ楽な気がします

HelloWorld.vue
<template>
  <div class="hello">
    <table class="ranking-table">
      <thead>
        <tr>
          <th>RANK</th>
          <th>NAME</th>
          <th>SCORE</th>
        </tr>
      </thead>
      <transition-group tag="tbody">
        <tr v-for="(post, index) in allScoreData" :key="post.id">
          <td :class="{newRecord: post.isNew}">{{index + 1}}</td>
          <td :class="{newRecord: post.isNew}">{{post.name}}</td>
          <td :class="{newRecord: post.isNew}">{{post.score}}</td>
        </tr>
      </transition-group>
    </table>
  </div>
</template>
〜〜

データを追加した際に、ランキングの行の追加でアニメーションを設定してますが、
こちらにはVueの transition を使っています

transizion
自分もよくわかっていないですが、Vueでいい感じにアニメーション(トランジション)を設定できるやつっぽいw

JS: Websocket通信

JSでは主にWebSocket通信と受信したデータを配列に格納する処理をしてます

HelloWorld.vue
〜〜
<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      allScoreData: [], // {id: Number, name: String, score: Number, isNew: Boolean}
    }
  },
  mounted() {
    const socket = new WebSocket('ws://localhost:5001');
    socket.onmessage = event => {
      this.addData(JSON.parse(event.data));
    }
  },
  methods: {
    addData(data) {
      // 既存のスコアのisNewをfalseにする (= 新規レコードのフラグを外す)
      const list = this.allScoreData.map(val => {
        return {
          id: val.id,
          name: val.name,
          score: val.score,
          isNew: false
        };
      });

      list.push({
        id: list.length + 1,
        name: data.name,
        score: Number(data.score),
        isNew: true
      });

      // 並び順をスコアの降順に変更
      this.allScoreData = list.sort((a,b) => (a.score > b.score ? -1 : 1))
    },
  }
}
</script>
〜〜

今回、WebSocketではNAMEとSCOREの2種類のデータを扱うので、JSONにしてやりとりしてます
サーバー上で動かすNode.jsは適当なポート(5001)でやってます

CSS: アニメーション

transitionの設定項目についてはドキュメントに書いてあります
適当にパラメータの数値をいじるとアニメーションが変化します

HelloWorld.vue
〜〜
<style>
.ranking-table {
  margin: auto;
  width: 50vw;
}
.newRecord {
  background-color: #ff7f50;
}

/* animation */
.v-leave-active,
.v-enter-active {
  transition: opacity .5s, transform .5s;
}
.v-leave-to,
.v-enter {
  opacity: 0;
  transform: translateX(200px);
}
.v-leave,
.v-enter-to {
  opacity: 1;
}
.v-move {
  transition: transform .5s;
}
</style>

サーバー側の準備

Node.jsで動かすプログラムもシンプル!
Websocket用ライブラリの ws をつかって、ひたすら待機しながらデータ来たら横に流すだけ

server.js
const ws = require('ws').Server;
const wsServer = new ws({ port: 5001 });

wsServer.on('connection', server => {
  server.on('message', message => {
    wsServer.clients.forEach(client => {
      client.send(message);
    });
  });
});

console.log('websocket起動中...');

接続するclient(ブラウザ)が複数あるので、forEach を使って接続してる全部のclientにデータを送り返してます
あとはこれは Node server.js で実行するのみ

補足

今回は送られてきたデータをただリアルタイムにランキング表示するだけなのですが、
実際に使おうとすると データを保存したい ってなると思います
(現状ではランキング側のブラウザを再読み込みするとリセットされるので)

その場合は AWSのDynamo WebDBとして扱いやすい kintone とか使うといいですよ!!(宣伝)

kintoneの使い方はこちらでまとめてます!!

▼ kintoneの使い方 (データベース編)
https://qiita.com/RyBB/items/daabb9b60d804ee2242f

おわりに

さくっと記事書くつもりがなんだかんだ説明が長くなってしまった・・・

今回はWebsocketにローカルのNode.jsサーバー使ってますが、AWSのAPI GatewayもWebSocketに使えるので
「API Gateway + Lambda + DynamoDB」って構成も可能です!(従量課金ですが)

ランキング画面は外部ディスプレイとかに表示しっぱなしにしておいて、IoTデバイスとかでデータ追加したらこっちも変わる!とか面白そう

それでは!≧(+・` ཀ・´)≦

12
10
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
12
10