1
2

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 5 years have passed since last update.

JavaScriptでビンゴを作る

Posted at

講義でくじ引きをするにあたり、いい機会なので作ってみました。プログラミングはてんで駄目なので目新しい機能はありませんが、復習用にメモしておきます。以下自分用メモなので諸々雑ですがうっかり通りすがった方はすみません、ご了承ください。
基本的な作り方は末尾にまとめた記事を参考にさせていただきました。

#概要
仕様は概ね下記の通り。スクリーンに写すのでPC前提です。クリックミスで同じ人が二度引かないように、スタートボタンとストップボタンを分けましたが、スマホ前提なら一個にした方が良いかも。
なお、即席なのでセットできる数値は半角数字のみです。来期も使う機会があったら入力チェックをつけよう。

  • ビンゴに使う数字の最大数を入力してセットする
  • スタートを押すと数字が周り、ストップで止まる
  • 同じ数字が出ない
  • 一度でた数字は色がつく

→DEMO
(不親切設計なので半角数字での指定以外は受け取りません。)
(音がなりますのでご注意ください。)

HTML

そんなに壮大な必要はなかったのでinput要素は半角数字2桁までに限定。多分CSS的に3桁まではなんとか?
ビンゴの番号用リスト(出た番号は黄色にする)id=numList、出た順に数字を並べる履歴用リストid=resultの2種類を用意。遊ぶだけならresultはいらないけど、今回は一応つけておこう。

html
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="reset.css">
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="bingo.js"></script>
<title>Bingo slot</title>
</head>
<body>
  <header>
    <div class="inner">
      <h1>BINGO SLOT</h1>
    </div>
  </header>
  <nav class="menu">
    <div class="inner">
      <form name="setmenu">
        <input type="text" name="dat" id="value" pattern="[1-9][0-9]" maxlength="2">
        <button type="button" id="setBtn">SET</button>
        <button type="button" id="resetBtn">RESET</button>
      </form>
    </div>
  </nav>
  <section class="main">
    <div class="inner">
      <div id="roulette"></div>
      <form id="playBtn">
        <button id="startBtn">START</button>
        <button id="stopBtn">STOP</button>
      </form>
      <audio id="dramroll" preload="auto">
        <source src="dramroll.mp3" type="audio/mp3">
      </audio>
      <audio id="pingpong" preload="auto">
        <source src="pingpong.mp3" type="audio/mp3">
      </audio>
    </div>
  </section>
  <section class="listSec">
    <div class="inner">
      <div>
        <ul class="clearfix" id="numList">
        </ul>
      </div>
      <div>
        <ul class="clearfix" id="result">
        </ul>
      </div>
    </div>
  </section>
  <fotter>
    <div class="inner">
      <address>Copyright(c) --, Allright Reserved.</address>
    </div>
  </fotter>
</body>
</html>

CSS

丸いとビンゴ感が出るかなという安直さ。即席感満載だけどま、まあ今回きりだから...。

CSS
@charset "UTF-8";
@import url('https://fonts.googleapis.com/css?family=Montserrat&display=swap');

html {
  background: #fff;
  font-size: 100%;
}
body {
  margin:0 auto;
  width: 100%;
  color: #0b3c5d;
  font-family: 'Montserrat', 'Hiragino Kaku Gothic Pro', Osaka, 'メイリオ', 'MS Pゴシック', 'MS PGothic', Verdana,Arial, sans-serif;
  text-align: center;
}
header{
  background: #333;
  color: #fff;
}
section.main {
  margin: 0 auto;
}
section.listSec {
  min-height: 300px;
  background: #f2eee2;
}
div.inner {
  margin: 0 auto;
  padding: 30px 0;
  width: 80%;
  max-width: 1000px;
}
fotter {
  clear: both;
}
h1 {
  font-size: 3.5rem;
  font-weight: bold;
}
form {
  display: flex;
  justify-content: center;
}
#value, #setBtn, #resetBtn {
  height: 2.5rem;
  box-sizing: border-box;
}
#value {
  padding: 10px 5px;
  width: 4rem;
  background-color: #fff;
  border: 2px solid #0b3c5d;
  font-size: 1.4rem;
  ime-mode: disabled;
  line-height: 2.5rem;
}
#setBtn, #resetBtn {
  margin-left: 10px;
  padding: 0px 20px;
  background: #0b3c5d;
  border: 2px solid #0b3c5d;
  color: #fff;
  font-size: 1.2rem;
}
#setBtn:hover, #resetBtn:hover {
  background: #f2eee2;
  color: #0b3c5d;
}
#setBtn:disabled, #resetBtn:disabled {
  border: 2px solid #f2eee2;
  background: #f2eee2;
  color: #fff;
}
#roulette {
  margin: 0 auto;
  width: 400px;
  height: 400px;
  background: #f2eee2;
  border-radius: 200px;
  font-size: 12rem;
  line-height: 400px;
}
#startBtn, #stopBtn {
  margin:20px;
  width: 120px;
  height: 120px;
  border-radius: 60px;
  font-size: 2rem;
  font-weight: bold;
  color: #fff;
}
#startBtn {
  background: #008f95;
}
#startBtn:disabled {
  border: 2px solid #008f95;
  background: #C6EBEC;
  color: #008f95;
}
#stopBtn {
  background: #e24e42;
}
#stopBtn:disabled {
  border: 2px solid #e24e42;
  background: #F0CCCA;
  color: #e24e42;
}
ul {
  margin-top: 30px;
  list-style-type: none;
  display: block;
  overflow: hidden;
}
li {
  margin: 8px;
  width:60px;
  height: 60px;
  line-height: 60px;
  border-radius: 30px;
  background: #fff;
  display: inline-block;
  font-size: 1.8rem;
}
li.hit {
  background: #fedc3d;
}
ul#numList li {
  float: left;
}
ul#result {
  color: #f2eee2;
}
ul#result li {
  float:left;
}
.clearfix::after {
  content: "";
  display: block;
  clear: both;
}

JavaScript

JavaScript
window.addEventListener('load', function bingo() {
  let setBtn = document.getElementById('setBtn');
  let startBtn = document.getElementById('startBtn');
  let stopBtn = document.getElementById('stopBtn');
  let resetBtn = document.getElementById('resetBtn');
  let result = document.getElementById('result');
  let position = 'beforeend';
  let dramroll = document.getElementById('dramroll');
  let pingpong = document.getElementById('pingpong');
  // ボタンの起動
  resetBtn.disabled = true;
  startBtn.disabled = true;
  stopBtn.disabled = true;
  // セットクリック
  function setBingo(){
    let max = parseInt(document.setmenu.dat.value, 10);    // 入力された値を数値に変換
    let numList = [...Array(max).keys()].map(i => ++i);    // max分の番号を入れた配列
    let el01 = document.querySelector('#numList');
    let el02 = document.querySelector('#result');
    // ボタンの起動
    setBtn.disabled = true;
    resetBtn.disabled = false;
    startBtn.disabled = false;
    // 番号リストを出力
    for(i = 0; i < max; i++) {
      let value = '<li>' + numList[i] + '</li>';
      el01.insertAdjacentHTML(position, value);    // 1からmaxまでのくじ番号リストを出力
    }
    // くじを作成
    for (i = max - 1; i >= 0; i--) {    // 配列内の整数をシャッフル
      let random = Math.floor(Math.random() * (i + 1));
      [numList[i], numList[random]] = [numList[random], numList[i]];
    }
    // スタートクリック
    function startBingo() {
      if (0 < numList.length) {
        stopBtn.disabled = false;
        startBtn.disabled = true;
        pingpong.pause();
        pingpong.currentTime = 0;
        dramroll.play();
        let status = true;    // ルーレットを起動
        roulette = setInterval(function() {
          let rouletteNum = Math.floor((Math.random() *max) + 1);    // maxまでのランダムな数を作成
          document.getElementById('roulette').innerHTML = rouletteNum;    // 表示
        }, 30);
      } else {
        ;
      }
    }
    startBtn.addEventListener('click', startBingo);
    // ストップクリック
    function stopBingo() {
      status = false;    // ルーレットを停止
      dramroll.pause();
      dramroll.currentTime = 0;
      pingpong.play();
      if (0 < numList.length) {
        stopBtn.disabled = true;
        startBtn.disabled = false;
        clearInterval(roulette);
        let current = numList.shift();    // 結果を取り出す
        document.getElementById('roulette').innerHTML = current;    // 結果をルーレット上に出力
        let value = '<li>' + current + '</li>';
        result.insertAdjacentHTML(position, value);    // 結果リストに当選番号を出力
        let hitNum = document.getElementById('numList').getElementsByTagName('li');    // くじ番号リストのli要素
        let hit = hitNum[current - 1];    // 当選番号と同じ番号のli要素
        hit.classList.add('hit');
        if(numList.length == 0) {
          startBtn.disabled = true;
        } else {
          ;
        }
      } else {
        ;
      }
    }
    stopBtn.addEventListener('click', stopBingo);
    // リセットクリック
    function resetBingo() {
      window.location.reload();
    }
    resetBtn.addEventListener('click', resetBingo);
  }
  setBtn.addEventListener('click', setBingo);
  // テキストボックスでエンターキーが押された場合にビンゴをセットする
  function keydown(e) {
    if(e.keyCode == 13) {
      document.getElementById('value').nextElementSibling.focus();
    }
  }
  document.getElementById('value').addEventListener('keydown', keydown);
}, false);

順番に確認

id取得とボタンの初期値を設定

まずはロードとともにボタンのidを取得。ついでに音源用と配置用も用意しちゃってますね。
セットボタン以外のボタンはdisabledtrueにして無効状態に。

JavaScript
// JavaScript Document
window.addEventListener('load', function bingo() {
  let setBtn = document.getElementById('setBtn');
  let startBtn = document.getElementById('startBtn');
  let stopBtn = document.getElementById('stopBtn');
  let resetBtn = document.getElementById('resetBtn');
  let result = document.getElementById('result');
  let position = 'beforeend';
  let dramroll = document.getElementById('dramroll');
  let pingpong = document.getElementById('pingpong');
  // ボタンの起動
  resetBtn.disabled = true;
  startBtn.disabled = true;
  stopBtn.disabled = true;

セットボタンをクリック

parseIntの第一引数に入力された値document.setmenu.dat.valueを、第二引数に10進数を表す10を指定して、最大数maxの値を取得。
抽選結果を入れる配列postListとmax分のくじを入れる配列numListを用意する。numListはスプレッド演算子でmax個分の要素を展開、keys()でインデックスを返してくれるので、map()で各要素に1ずつ足したら1からmaxまでの値が順に入ったお行儀のいい配列が誕生。これで番号リスト用の配列ができたので、あとはfor文で出力。

今度はくじ用。Math.random()numListの中身をシャッフル。フィッシャー–イェーツのアルゴリズムについては下記参考。

JavaScript
  // セットクリック
  function setBingo(){
    let max = parseInt(document.setmenu.dat.value, 10);    // 入力された値を数値に変換
    let numList = [...Array(max).keys()].map(i => ++i);    // max分の番号を入れた配列
    let el01 = document.querySelector('#numList');
    let el02 = document.querySelector('#result');
    // ボタンの起動
    setBtn.disabled = true;
    resetBtn.disabled = false;
    startBtn.disabled = false;
    // 番号リストを出力
    for(i = 0; i < max; i++) {
      let value = '<li>' + numList[i] + '</li>';
      el01.insertAdjacentHTML(position, value);    // 1からmaxまでのくじ番号リストを出力
    }
    // くじを作成
    for (i = max - 1; i >= 0; i--) {    // 配列内の整数をシャッフル
      let random = Math.floor(Math.random() * (i + 1));
      [numList[i], numList[random]] = [numList[random], numList[i]];
    }

スタートクリック

スタート用の関数startBingoを作る。もしnumList.length(配列の要素数)が0より大きければ、実行する。2回目以降繰り返すときにストップ時の「デデン!」が重なったままドラムロールが鳴ると姦しいのでまずはpingpongを止めて、ついでにpingpong.currentTime = 0;で次のデデン!のために巻き戻しておく。そしてdramrollを鳴らす。デレレレレレ...
ルーレットも回す。詳細は参考文献を確認のこと。1からmaxまでの数字をグルグル...速さはとりあえず30ミリ秒に。
add.EventListenerでクリックしたら動いておくれ。

JavaScript
    // スタートクリック
    function startBingo() {
      if (0 < numList.length) {
        stopBtn.disabled = false;
        startBtn.disabled = true;
        pingpong.pause();
        pingpong.currentTime = 0;
        dramroll.play();
        let status = true;    // ルーレットを起動
        roulette = setInterval(function() {
          let rouletteNum = Math.floor((Math.random() *max) + 1);    // maxまでのランダムな数を作成
          document.getElementById('roulette').innerHTML = rouletteNum;    // 表示
        }, 30);
      } else {
        ;
      }
    }
    startBtn.addEventListener('click', startBingo);

ストップクリック

ストップ用の関数stopBingoを作る。目と耳がうるさいのでルーレットとドラムロールを止めて「デデン!」を鳴らす。もしnumList.length(配列の要素数)が0より大きければ、実行する。ルーレットをクリアしてシャッフル済みのnumListから要素をひとつ取り出してルーレット上にドーンと出力。
履歴用の<ul id="result"></ul>のli要素を出力しておく。
出力しておいたnumListの番号に色をつけたい。li要素を取得して配列に突っ込んだhitNumの誕生を宣言する。hitcurrent-1番目の要素をさらに宣言して、class="hit"をつけて無事色付け終わり。
numListが0になったら、スタートボタンを止めておく。add.EventListenerでクリックしたらよろしく。

JavaScript
// ストップクリック
    function stopBingo() {
      status = false;    // ルーレットを停止
      dramroll.pause();
      dramroll.currentTime = 0;
      pingpong.play();
      if (0 < numList.length) {
        stopBtn.disabled = true;
        startBtn.disabled = false;
        clearInterval(roulette);
        let current = numList.shift();    // 結果を取り出す
        document.getElementById('roulette').innerHTML = current;    // 結果をルーレット上に出力
        let value = '<li>' + current + '</li>';
        result.insertAdjacentHTML(position, value);    // 結果リストに当選番号を出力
        let hitNum = document.getElementById('numList').getElementsByTagName('li');    // くじ番号リストのli要素
        let hit = hitNum[current - 1];    // 当選番号と同じ番号のli要素
        hit.classList.add('hit');
        if(numList.length == 0) {
          startBtn.disabled = true;
        } else {
          ;
        }
      } else {
        ;
      }
    }
    stopBtn.addEventListener('click', stopBingo);

リセットボタン

リセットボタンは単純にクリックしたらリロードにしました。ここまででセットボタンの関数がおしまい。add.EventListenerでクリックしたらよろしく。

JavaScript
// リセットクリック
    function resetBingo() {
      window.location.reload();
    }
    resetBtn.addEventListener('click', resetBingo);
  }
  setBtn.addEventListener('click', setBingo);

テキストボックスのエンターキーダウン処理

動作テスト時にうっかりキーダウンでリロードしてあ...ってなったので一応これだけはつけて置く。本当はきちんと入力制限とエラー表示つけたほうがいいでしょう。

JavaScript
setBtn.addEventListener('click', setBingo);
  // テキストボックスでエンターキーが押された場合にビンゴをセットする
  function keydown(e) {
    if(e.keyCode == 13) {
      document.getElementById('value').nextElementSibling.focus();
    }
  }
  document.getElementById('value').addEventListener('keydown', keydown);
}, false);

参考記事

これがなければ始まらない。本当にありがとうございました。

配列についてはこちらが大変わかりやすくまとめられていて素敵です。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?