講義でくじ引きをするにあたり、いい機会なので作ってみました。プログラミングはてんで駄目なので目新しい機能はありませんが、復習用にメモしておきます。以下自分用メモなので諸々雑ですがうっかり通りすがった方はすみません、ご了承ください。
基本的な作り方は末尾にまとめた記事を参考にさせていただきました。
#概要
仕様は概ね下記の通り。スクリーンに写すのでPC前提です。クリックミスで同じ人が二度引かないように、スタートボタンとストップボタンを分けましたが、スマホ前提なら一個にした方が良いかも。
なお、即席なのでセットできる数値は半角数字のみです。来期も使う機会があったら入力チェックをつけよう。
- ビンゴに使う数字の最大数を入力してセットする
- スタートを押すと数字が周り、ストップで止まる
- 同じ数字が出ない
- 一度でた数字は色がつく
→DEMO
(不親切設計なので半角数字での指定以外は受け取りません。)
(音がなりますのでご注意ください。)
HTML
そんなに壮大な必要はなかったのでinput要素は半角数字2桁までに限定。多分CSS的に3桁まではなんとか?
ビンゴの番号用リスト(出た番号は黄色にする)id=numList
、出た順に数字を並べる履歴用リストid=result
の2種類を用意。遊ぶだけならresultはいらないけど、今回は一応つけておこう。
<!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
丸いとビンゴ感が出るかなという安直さ。即席感満載だけどま、まあ今回きりだから...。
@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
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を取得。ついでに音源用と配置用も用意しちゃってますね。
セットボタン以外のボタンはdisabled
をtrue
にして無効状態に。
// 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
の中身をシャッフル。フィッシャー–イェーツのアルゴリズムについては下記参考。
// セットクリック
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
でクリックしたら動いておくれ。
// スタートクリック
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
の誕生を宣言する。hit
でcurrent-1
番目の要素をさらに宣言して、class="hit"
をつけて無事色付け終わり。
numList
が0になったら、スタートボタンを止めておく。add.EventListener
でクリックしたらよろしく。
// ストップクリック
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
でクリックしたらよろしく。
// リセットクリック
function resetBingo() {
window.location.reload();
}
resetBtn.addEventListener('click', resetBingo);
}
setBtn.addEventListener('click', setBingo);
テキストボックスのエンターキーダウン処理
動作テスト時にうっかりキーダウンでリロードしてあ...ってなったので一応これだけはつけて置く。本当はきちんと入力制限とエラー表示つけたほうがいいでしょう。
setBtn.addEventListener('click', setBingo);
// テキストボックスでエンターキーが押された場合にビンゴをセットする
function keydown(e) {
if(e.keyCode == 13) {
document.getElementById('value').nextElementSibling.focus();
}
}
document.getElementById('value').addEventListener('keydown', keydown);
}, false);
参考記事
これがなければ始まらない。本当にありがとうございました。
- HTML / CSS / JavaScript(jQuery)でビンゴゲームを作成
- JavaScript - JavaScriptでビンゴゲームを作る
- JavaScriptでビンゴマシンを作ってみた。
- JavaScriptでビンゴをつくる
配列についてはこちらが大変わかりやすくまとめられていて素敵です。