JavaScriptでビンゴゲームを作ってみました。
#仕様
- カードは25マス。
- 「CARD SET」を押すと,カードにランダムな数字が入る。「CARD SET」は無効化する。
- 「CHOOSE NUMBER」を押すと,ランダムな数字が結果として表示される。
- 結果と一致するカードのマスは色が変わる。
- 履歴欄に結果の数字を順番に追加していく。
- 全ての数字を選び終わると,アラートで終了を知らせる。「CHOOSE NUMBER」は無効化する。
- 「RESET」を押すと,最初の状態に戻る。
#コード
####HTML
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ビンゴゲーム | JS</title>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Goldman&family=Press+Start+2P&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles/style.css"/>
</head>
<body>
<h1 class="title">!!!!! BINGO GAME !!!!!</h1>
<div class="container">
<div class="left-container">
<div class="left-container__btns">
<button id="setBtn" class="btn">CAED SET</button>
<button id="chooseBtn" class="btn">CHOOSE NUMBER</button>
<button id="resetBtn" class="btn">RESET</button>
</div>
<div class="left-container__roulette" id="roulette">
</div>
<div class="left-container__history">
<h2>HISTORY_</h2>
<ul class="history-list" id="history-list"></ul>
</div>
</div>
<div class="card-container">
<h2>CARD</h2>
<div id="card">
</div>
</div>
</div>
<script src="scripts/selectNumber.js"></script>
<script src="scripts/main.js"></script>
</body>
</html>
####CSS(SCSS)
SCSS
styles/style.scss
$cWhite: #ffffff;
$cYellow: #f8cc2f;
$cBlackGray: #2c2c2c;
$cRed: #e00000;
$cGreen: #03a305;
$cGray: rgb(192, 191, 191);
body {
margin: 0;
text-align: center;
background-color: $cYellow;
width: 100vw;
height: 100vh;
box-sizing: border-box;
position: relative;
}
.title {
font-family: 'Press Start 2P', cursive;
color: $cRed;
font-size: 2rem;
text-shadow: 3px 3px 0px $cWhite;
}
.container {
width: 90%;
margin: 0 auto;
padding-top: 20px;
max-width: 1000px;
display: flex;
justify-content: space-between;
align-items: center;
}
.left-container {
width: 48%;
&__btns {
display: flex;
flex-direction: column;
& .btn {
display: inline-block;
box-sizing: border-box;
padding-top: 15px;
padding-bottom: 15px;
margin: 15px 0;
width: 100%;
background-color: #04c706;
border: outset 6px $cGreen;
outline: none;
font-size: 18px;
color: $cWhite;
font-family: 'Press Start 2P', cursive;
cursor: pointer;
&:disabled {
background-color: rgb(172, 172, 172);
color: $cWhite;
border: inset 6px $cGray;
}
}
}
&__roulette {
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
margin: 15px auto;
width: 200px;
height: 120px;
background-color: $cWhite;
border: solid 6px $cRed;
border-radius: 50%;
font-size: 42px;
font-family: 'Press Start 2P', cursive;
color: $cRed;
}
&__history {
box-sizing: border-box;
padding: 20px;
width: 100%;
height: 180px;
background-color: $cBlackGray;
border: solid 3px $cWhite;
& h2 {
margin: 0;
text-align: left;
font-family: 'Press Start 2P', cursive;
font-size: 18px;
color: $cWhite;
}
& ul {
margin: 0 auto;
padding: 10px 0 0 0;
font-family: 'Press Start 2P', cursive;
color: $cWhite;
list-style: none;
display: flex;
flex-wrap: wrap;
& li {
width: 12%;
font-size: 0.8rem;
line-height: 1.5rem;
}
}
}
}
.card-container {
width: 48%;
height: 600px;
background-color: $cWhite;
box-shadow: 9px 9px 0px $cBlackGray;
& h2 {
margin: 35px auto;
padding-left: 0.7em;
font-family: 'Press Start 2P', cursive;
font-size: 35px;
color: $cBlackGray;
letter-spacing: 0.7em;
}
& #card {
margin: 20px;
width: 90%;
display: flex;
flex-wrap: wrap;
& .cell {
box-sizing: border-box;
width: 20%;
background-color: $cWhite;
border: solid 3px $cGray;
position: relative;
&::before {
content: '';
display: block;
padding-top: 100%;
}
& .cell-inner {
font-family: 'Press Start 2P', cursive;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
font-size: 1.2rem;
color: $cBlackGray;
}
&.opened {
background-color: $cRed;
& .cell-inner {
color: $cWhite;
}
}
}
}
}
####JavaScript
scripts/selectNumber.js
class SelectNumber {
constructor(arrBox) {
this.arrBox = arrBox;
this.arr = [];
this._setNumber();
this._assignNumber(arrBox);
}
_setNumber() {
for(let i = 1; i <= 75; i++) {
this.arr.push(i);
}
}
_assignNumber() {
for(let j = 0, len = this.arr.length; j < 25; j++, len--) {
let rdmNumber = Math.floor(Math.random() * len);
this.arrBox.push(this.arr[rdmNumber]);
this.arr[rdmNumber] = this.arr[len - 1];
}
}
}
scripts/main.js
window.addEventListener('DOMContentLoaded', function() {
const card = document.getElementById('card');
const setBtn = document.getElementById('setBtn');
const chooseBtn = document.getElementById('chooseBtn');
const roulette = document.getElementById('roulette');
const historyList = document.getElementById('history-list');
const resetBtn = document.getElementById('resetBtn');
let countClick = 0;
let cardArr = [];
let resultArr = [];
setBtn.disabled = false;
chooseBtn.disabled = false;
setBtn.addEventListener('click', function() {
const select1 = new SelectNumber(cardArr);
const select2 = new SelectNumber(resultArr);
for (let i = 0; i < 25; i++) {
const cell = document.createElement('div');
const cellInner = document.createElement('div');
cell.classList.add('cell');
cellInner.classList.add('cell-inner');
cellInner.textContent = cardArr[i];
cell.setAttribute('name', cardArr[i]);
cell.appendChild(cellInner);
card.appendChild(cell);
};
setBtn.disabled = true;
});
chooseBtn.addEventListener('click', function() {
if(countClick === 25) {
alert('FINISH!!!!');
chooseBtn.disabled = true;
} else {
const result = resultArr[countClick];
roulette.textContent = result;
const li = document.createElement('li');
li.textContent = result;
historyList.appendChild(li);
const resultCell = document.querySelector(`.cell[name = "${result}"]`);
if(resultCell !== null) {
resultCell.classList.add('opened');
}
countClick++;
}
});
resetBtn.addEventListener('click', function() {
window.location.reload();
});
});
#解説
####ランダムな数字を選ぶ
scripts/selectNumber.js
class SelectNumber {
constructor(arrBox) {
this.arrBox = arrBox;
this.arr = [];
this._setNumber();
this._assignNumber(arrBox);
}
_setNumber() {
for(let i = 1; i <= 25; i++) {
this.arr.push(i);
}
}
_assignNumber() {
for(let j = 0, len = this.arr.length; j < 25; j++, len--) {
let rdmNumber = Math.floor(Math.random() * len);
this.arrBox.push(this.arr[rdmNumber]);
this.arr[rdmNumber] = this.arr[len - 1];
}
}
}
1〜75のランダムな数字を選ぶclass。
正確には,1〜75の中から25個を重複なくランダムに選び,渡された配列に順に格納していくclass。
今回はこのclassで,「カード用の配列」と「結果用の配列」を作成。
この仕組みは何方かのブログで見つけ利用させて頂いたのですが,この記事を書くためにもう一度探したら見当たりませんでした。見つけ次第記載します。申し訳ありません…
####「CARD SET」を押した時の動作
scripts/main.js
setBtn.addEventListener('click', function() {
const select1 = new SelectNumber(cardArr);
const select2 = new SelectNumber(resultArr);
for (let i = 0; i < 25; i++) {
const cell = document.createElement('div');
const cellInner = document.createElement('div');
cell.classList.add('cell');
cellInner.classList.add('cell-inner');
cellInner.textContent = cardArr[i];
cell.setAttribute('name', cardArr[i]);
cell.appendChild(cellInner);
card.appendChild(cell);
};
setBtn.disabled = true;
});
- 「カード用の配列」と「結果用の配列」を準備する。
- カードのマスになるHTMLを組み立てる。
- この後で選ばれたマスの表示を変えるのに使う為,
cell
に自分の番号のname属性を付けておく。 - 「CARD SET」は無効化する。
####「CHOOSE NUMBER」を押した時の動作
scripts/main.js
chooseBtn.addEventListener('click', function() {
if(countClick === 25) {
alert('FINISH!!!!');
chooseBtn.disabled = true;
} else {
const result = resultArr[countClick];
roulette.textContent = result;
const li = document.createElement('li');
li.textContent = result;
historyList.appendChild(li);
const resultCell = document.querySelector(`.cell[name = "${result}"]`);
if(resultCell !== null) {
resultCell.classList.add('opened');
}
countClick++;
}
});
- if文でクリック数を監視。
- 結果用の配列の「クリック数」番目の数字を結果として表示する。
- 履歴欄に結果の数字を追加。
- 結果と一致するname属性値を持つ
cell
を取得→classの追加で表示変更。 - 25回クリックしていたら(26回目を押したら),「FINISH!!!!」のアラートが出る。
- 「CHOOSE NUMBER」を無効化。
####(2021/3/28更新 改善)
- 数字がランダムで選ばれる範囲:25→75に変更
- 結果と一致するname属性値を持つ
cell
が,card上に存在するかif文で調べる。存在すれば(resultCellがnullではなければ)classを追加。
#反省,改善点
- 数字を選ぶ際に,ドラムロール式にしたかった。
- カードのリセットがページを再読み込みする方法になっている。それぞれの要素をリセットするような方法にしたい。(どっちの方が負担や効率面で良いのか分かりませんが…)