こんにちは。今回はスロットマシーンです。
ドットインストールの「JavaScriptでスロットマシンを作ろう」にアレンジを加えて、以下のことを実装しました。
・リーチフラグの導入し、それが立ったら画像を表示する(これが立たないと7を狙っても外れる)
・777を揃えると、上部パネルの画像を変更する
・777成立後の1ゲームはベルが揃う
・ベルが揃い、終了したらもとに戻る
周りも多少装飾してスロットマシーンぽくしてみました。
そうしてできた物が以下になります。
完成版
はい。なんだかどこかで見たような気がしないでもないですね。
ご想像の通り、ピエロ的なアレを参考にしました。
上記のアレンジ実装機能もそれに沿ったものです。なのでアレをご存じの方はアレの仕様に似てると思ってください。
では作って行きたいと思います。
今回も以前と同様に、初心者ならではのつまづきを含めて解説していきたいと思います。
HTML
まずはhtmlです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>slot machine</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="wrapper">
<header id="result_area" class="none"><img></header>
<main></main>
<div class="flex">
<div id="gogo" class="gogo_off"><img></div>
<div id="spin">SPIN</div>
</div>
<footer></footer>
</div>
<script src="js/main.js"></script>
</body>
</html>
解説するとすれば、
wrapper
= 大枠
header
= 上部パネル(ボーナス中の画像を表示するエリア)
main
= スロット部分
flex
= ランプとSPINボタン
footer
= パネル
というところです。
CSSについては特に難しいことはしていません。基本的なレイアウトと、透明度くらいのものです。
書くと長くなるので、一番最後に貼り付けたいと思います。
それではJavaScriptを書いていきます。
クラスを使ってパネルを描画する
パネルとは、絵柄がくるくる回るエリアのことです。
この絵柄とストップボタンをsection
としてひとまとめにして、それを3つ並べることでmain
を構成しています。main
の中身は以下のように出力されるので、こうなるようにクラスを設定しましょう。
<main>
<section class="panel">
<img src="img/cherry.jpg">
<div class="stop">STOP</div>
</section>
<section class="panel">
<img src="img/cherry.jpg">
<div class="stop">STOP</div>
</section>
<section class="panel">
<img src="img/cherry.jpg">
<div class="stop">STOP</div>
</section>
</main>
ではsection
を作るPanelクラスを作りましょう。
内容は、生成するsection
にPanelクラスをつけて、その中にimg
とSTOP
ボタンを作り、それらをmain
に対して追加していきます。
class Panel {
constructor() {
const section = document.createElement('section'); // section要素を生成
section.classList.add('panel'); // panelクラスをつける
this.img = document.createElement('img'); // プロパティとして設定
this.img.src = 'img/seven.jpg'; // ここではとりあえずseven.jpgに設定
this.stop = document.createElement('div'); // プロパティとして設定
this.stop.textContent = 'STOP'; // テキストをSTOPに設定
this.stop.classList.add('stop'); // STOPクラスを追加
section.appendChild(this.img); // Sectionの子要素として追加
section.appendChild(this.stop); // Sectionの子要素として追加
const main = document.querySelector('main'); // mainを取得
main.appendChild(section); // main内に追加
}
}
これでひとまずOKです。これをもってnew Panel
とインスタンスを生成すれば、ページに上記の要素が追加されます。
これらの要素はまとめて扱えるように、panelsという配列を宣言して、その中で3つインスタンスを生成します。
class Panel {
constructor() {
const section = document.createElement('section');
section.classList.add('panel');
this.img = document.createElement('img');
this.img.src = 'img/seven.jpg';
this.stop = document.createElement('div');
this.stop.textContent = 'STOP';
this.stop.classList.add('stop');
section.appendChild(this.img);
section.appendChild(this.stop);
const main = document.querySelector('main');
main.appendChild(section);
}
}
const panels = [ // インスタンス生成
new Panel(),
new Panel(),
new Panel(),
];
これでインスタンスが3つ並んでmain
内に表示されていると思います。
ここで私がつまづいたところを解説します。
配列宣言=実行
上記のpanelsという配列を宣言したところなのですが、私の意識の中では配列宣言というのは、要はDB生成のようなもので、あとで使うものをいまのうちに用意しておくというものでした。
従って、このあとに例えばforEachなどを使って呼び出さない限り実行されないと思っていたのですが、どうやら勘違いをしていました。実際は**配列を使ってクラスから各インスタンスを生成する場合は、宣言と同時に実行される(constructorの呼び出し)**ようです。
配列を使ってクラスからインスタンス生成は初めてだったので、ここがどうしても理解できず、人に聞いてようやくわかりました。もし同じ初心者の方で同じような点に躓いた方がいたら参考になると嬉しいです。
では戻ります。
SPINボタンを使って各パネルの画像を回転させる
SPINボタンはidを設定してあるので、それを取得し、クリックイベントを追加します。
イベントはクリックしたら配列panelsの中の各要素をforEachを使って回し、panelで受け取ってスピンの処理をするように書きます。処理はpanelクラス内でspin()
としてまとめましょう。
const spin = document.getElementById('spin'); // id="spin"取得
spin.addEventListener('click', () => { // クリックイベント追加
panels.forEach(panel => { // 配列panelsを回して
panel.spin(); // panelクラス内のspin()を実行
});
});
ではpanelクラスの中でspin()を設定します。
constructorの下にイメージのsrc要素をランダムにするという処理を追加しましょう。
またそのランダムにする処理は別に設定します。
class Panel {
constructor() {
const section = document.createElement('section');
section.classList.add('panel');
this.img = document.createElement('img');
this.img.src = 'img/seven.jpg';
this.stop = document.createElement('div');
this.stop.textContent = 'STOP';
this.stop.classList.add('stop');
section.appendChild(this.img);
section.appendChild(this.stop);
const main = document.querySelector('main');
main.appendChild(section);
}
getRandomImage() {
return images[Math.floor(Math.random() * images.length)]; // 配列imagesからランダムにイメージを取得
}
spin() { // spinをクリックされたら
this.img.src = this.getRandomImage(); // getRandomImage()を実行
}
}
配列imagesはjsファイルの一番上にでも書いておきます。
const images = [ // 画像の配列
'img/seven.jpg',
'img/bell.jpg',
'img/cherry.jpg',
'img/bar.jpg',
'img/grape.jpg',
'img/piero.jpg',
'img/sai.jpg',
];
これにより「SPIN」ボタンが押されるたびにランダムに画像が切り替わるようになりました。
ちなみにconstructor
内のthis.img.src = 'img/seven.jpg'; // ここではとりあえずseven.jpgに設定
としたところも、getRandomImage()
を使うと初期値もランダムにできるので書き換えましょう。
this.img.src = this.getRandomImage(); // 初期値もランダムに
次はこれを自動でくるくる回るようにし、それを「STOP」ボタンで止められるようにしましょう。
「SPIN」ボタンと「STOP」ボタン
まずは自動で回るようにするには、setTimeout
を使って、spin()を繰り返します。
spin() {
this.img.src = this.getRandomImage();
this.timeoutId = setTimeout(() => { // ランダム表示処理を繰り返す
this.spin();
}, 600); // とりあえず600ミリ秒ごとで
}
次はこれを「STOP」ボタンで止めます。そのためにはボタンにクリックイベントとしてclearTimeout()
を追加します。
clearTimeout()
には止めるべきsetTimeout
の引数が必要です。なのでまずそれをtimeoutId
として設定します。これを使ってsetTimeout
の返り値をclearTimeout()
に渡すことで、回転を止めるようにします。
class Panel {
constructor() {
const section = document.createElement('section');
section.classList.add('panel');
this.img = document.createElement('img');
this.img.src = this.getRandomImage();
this.timeoutId = undefined; // 返り値用に用意 初期値はundefinedで良い
this.stop = document.createElement('div');
this.stop.textContent = 'STOP';
this.stop.classList.add('stop');
this.stop.addEventListener('click', () => { // 「STOP」ボタンのイベント追加
clearTimeout(this.timeoutId); // this.timeoutIdを受け取ってそれを止める
});
section.appendChild(this.img);
section.appendChild(this.stop);
const main = document.querySelector('main');
main.appendChild(section);
}
getRandomImage() {
return images[Math.floor(Math.random() * images.length)];
}
spin() {
this.img.src = this.getRandomImage();
this.timeoutId = setTimeout(() => { // 返り値をthis.timeoutIdとする
this.spin();
}, 50);
}
これで「STOP」ボタンで回転を止めることができました。
「SPIN」ボタンと違って、「STOP」ボタンは個々の要素を止めるので、panelクラス内にメソッドとして書くんですね。
停止させた要素を判定する
まず停止させた絵柄が、他の二枚と揃わなかった時に、その要素を薄くするようにしてみましょう。
要素を判定し、他の二枚と揃わなかった時に処理する
具体的には、
・停止させた枚数をカウント
・残り0枚になったら結果判定(の関数呼び出し)
・他の2枚と比較する
・一致しなかったら薄くする
という流れになります。
停止させた枚数のカウントはpanelsLeft
という変数名にして、初期値を3にし、「STOP」ボタンを押すたびにひとつ減らすという方法を取ります。そのカウントが0になったら結果判定checkResult()
をします。これはconst spinの上にでも書いておきます。
let panelsLeft = 3; // 残りの枚数
カウントを減らす文と結果判定は、「STOP」ボタンのクリックイベントに追加します。
this.stop.addEventListener('click', () => {
clearTimeout(this.timeoutId);
panelsLeft--; // 残り枚数カウントを減らす
if (panelsLeft === 0) { // 0になったら
checkResult(); // 結果判定関数を呼び出し
}
});
そしてこのcheckResult()
関数ですが、この「STOP」ボタンだけでなく、全ての要素について判定するので、panelクラス構文内ではなく、外に書きましょう。
内容は、checkResult()
が呼び出されたら、各パネルをひとつずつ調べて、他のパネルと比較します。
そのために用意するメソッドがふたつ。他の2つと比較するためのisUnmatched
メソッドと、一致しなかった場合に呼び出すunmatch
メソッドです。これらを使ったこのように書いておきます。
function checkResult() {
if (panels[0].isUnmatched(panels[1], panels[2])) {
panels[0].unmatch();
}
if (panels[1].isUnmatched(panels[0], panels[2])) {
panels[1].unmatch();
}
if (panels[2].isUnmatched(panels[0], panels[1])) {
panels[2].unmatch();
}
}
まずisUnmatched
です。これは個々に判定するので、panelクラス内に書くと良いでしょう。
引数として、他の要素をp1とp2で受け取ります。判定する要素のイメージのsrcプロパティthis.img.src
が他の要素のイメージのsrcプロパティと異なっていたらtrue、そうでなかったらfalseを返すという処理です。
ここでは両方の要素が共に異なっていたらtrueなので論理演算子のAND&&
を使います。
this.img.src !== p1.img.src
かつ this.img.src !== p2.img.src
ですね。
isUnmatched(p1, p2) {
if (this.img.src !== p1.img.src && this.img.src !== p2.img.src) {
return true;
} else {
return false;
}
}
ただし、このような書き方の場合、この条件自体をreturnしてあげれば同じ結果が得られます。従って
isUnmatched(p1, p2) {
return this.img.src !== p1.img.src && this.img.src !== p2.img.src;
}
これでいいわけですね。
次にunmatch
メソッドです。他とマッチしなかった場合は薄くしたいので、cssにunmatched
クラスを作ってopacityを0.5くらいにしておいて、それをclassListで追加すれば良いと思います。※cssは省略
unmatch() {
this.img.classList.add('unmatched');
}
これで結果判定で薄くする処理ができました。
ボタンを制御する
さて、ボタンの処理がかけたところで、ちゃんと動くように制御する必要があります。
なぜなら「SPIN」ボタンを連打すると「STOP」ボタン一回では止まらなくなっているからです。
これらの問題に対処するには、前回もやった一回押したらもう押せなくするを採用します。
まずビジュアル的にも押せないようにしたいので、押したら暗くしましょう。
とりあえずCSSにinactiveクラスというのでも作って、unmatchクラスと同じようにopacityを0.5にでもしておきます。
このクラスをclassList
で追加する処理はspin()のところですね。
「SPIN」ボタンを押した時にinactiveクラスがついているかチェックして、ついていたら以降の処理をしない、ついていなかったらinactiveクラスを追加した上で処理を行う、です。
spin.addEventListener('click', () => {
if (spin.classList.contains('inactive')) { // inactiveクラスがついてるかチェック
return; // ついていたらそのままreturn
}
spin.classList.add('inactive'); // inactiveクラスを追加する
panels.forEach(panel => {
panel.spin();
});
});
inactiveクラスがついてたらそのままreturnなので、forEachもしない、つまり絵柄回転もしないということですね。
これをそのまま「STOP」ボタンにも使います。
this.stop.addEventListener('click', () => {
if (this.stop.classList.contains('inactive')) { // inactiveクラスがついてるかチェック
return; // ついていたらそのままreturn
}
this.stop.classList.add('inactive'); // inactiveクラスを追加する
clearTimeout(this.timeoutId);
panelsLeft--;
if (panelsLeft === 0) {
checkResult();
}
});
このやり方は一回しか押してほしくない時に使えそうですね。
その他の整備
ここまで作ってきたうえで、まだいくつか問題が残っていますので、それらを片付けていきます。
連続で遊べるようにする
「STOP」ボタンを3つ押した後、もう一度遊べるようにします。今のままだと、「SPIN」「STOP」ボタンも押せないし、絵柄は判定で薄くなってたりで遊べません。
そのため、まず「SPIN」ボタンなどのinactiveクラスを取ることにします。場所は結果判定の後が良いでしょう。つまりここですね。
this.stop.addEventListener('click', () => {
if (this.stop.classList.contains('inactive')) {
return;
}
this.stop.classList.add('inactive');
clearTimeout(this.timeoutId);
panelsLeft--;
if (panelsLeft === 0) {
checkResult();
spin.classList.remove('inactive'); // 「SPIN」ボタンのinactiveクラスを外す
panelsLeft = 3; // 残り枚数カウントを3に戻す
}
});
「STOP」ボタンのinactiveクラスを外すのは、次に「SPIN」ボタンを押したときが良いでしょう。
その処理はactivate
メソッドとしてクラス内に処理を書きます。まずは
spin.addEventListener('click', () => {
if (spin.classList.contains('inactive')) {
return;
}
spin.classList.add('inactive');
panels.forEach(panel => {
panel.activate(); // STOPボタンのinactiveクラスを外す
panel.spin();
});
});
とし、クラス内の最後あたりに以下を書きます。
activate() {
this.img.classList.remove('unmatched'); // 画像を暗くするためのunmatchedを外す
this.stop.classList.remove('inactive'); // ボタンのinactiveを外す
}
これでOKです。
SPINする前にSTOPボタンが押せてしまう
これもなんだか変ですよね。そのため、一番最初のボタンを生成する段階で、inactiveクラスをつけてしまいます。
this.stop.classList.add('stop', 'inactive');
はい。これで概ねスロットマシーンとしての動きは完成です。
あとはジャグ○ーっぽく仕上げていきたいと思います。
ジャグ○ーっぽく仕上げる
最初にも書きましたが、それっぽく仕上げるために、追加する処理がいくつかあります。
・リーチフラグの導入し、それが立ったら画像を表示する(これが立たないと7を狙っても外れる)
・777を揃えると、上部パネルの画像を変更する
・777成立後の1ゲームはベルが揃う
・ベルが揃い、終了したらもとに戻る
以上です。それに伴い、フラグを用意する必要があるのでこれも解説します。
状態管理
・hitNumとgogoNum
今までのものだと、いつでも777が揃います。それではつまらないので、あらかじめ用意した定数hitNumと、スロットを回すごとにランダムで設定されるgogoNumが一致したときに初めて777を揃えることできるようにしましょう。
・リーチフラグ(reachFlag)
定数hitNumと回転するごとにランダムで設定するgogoNumが一致したらリーチフラグ(reachFlag)が立つので、これがたったら、画面上にそのことを知らせるため画像を切り替えます。
(なんと神々しい・・)
それをif文で判定し、trueなら777を揃えることが可能になり、その処理を実行できます。
ちなみにfalseの場合は、777を狙っても滑ってチェリーになるようにします。
・777が揃ったフラグ(get777)
777が揃った時に立つフラグです。これが立っていると次のゲームは、ベルが揃います。元でいうボーナスゲームのようなものです。1ゲームだけなのですが、これを入れたかったのでこのフラグをもたせました。ちなみにこれが立っている間は上部パネルが変わるようになっています。
流れとしては以下のようになります。
※少々書き方があやしいですが気になさらずに
フラグ判定
では上記に必要なものを宣言しておきましょう。
const hitNUm = 0; // 当たりのナンバー
let gogoNum = 0; // 回転ごとにランダムで決まるナンバー
let reachFlag = 0; // リーチフラグ管理
let get777 = 0; // ボーナスゲーム管理
まずは回転ごとに決まるナンバーです。これは全停止したら決まるので、「STOP」ボタンのクリックイベント内、panelsLeftが0になったらのところに追加します。ここでは検証のしやすいように、1/3で当たるようにしました。
それで決まるgogoNum
を持ってcheckResult
で色々と判定することにしたいので引数にgogoNum
を入れます。
if (panelsLeft === 0) {
gogoNum = Math.floor(Math.random() * 3); // 回転ごとにランダムで決まるナンバー
checkResult(gogoNum); // ランダムな数 gogoNum を持ってcheckResultする
spin.classList.remove('inactive');
panelsLeft = 3;
}
});
次はcheckResult
です。
hitNumとgogoNumが一致しているかをチェックして、それに合わせた処理を書きます。
各処理は
・reachFlagをたたせる
・reachFlagが立ったよ、ということを画像で知らせるための処理を行う
後者についてはgogoFlash()
という関数を作って呼び出すことにします。
function checkResult(gogoNum) { // gogoNumを受け取って以下の処理
if (hitNUm === gogoNum) { // hitNUm と gogoNum が一致していたら
gogoFlash(1); // gogoFlash()関数を呼び出す
reachFlag = 1; // reachFlagを1にする(フラグを立たせる)
}
if (panels[0].isUnmatched(panels[1], panels[2])) {
panels[0].unmatch();
}
if (panels[1].isUnmatched(panels[0], panels[2])) {
panels[1].unmatch();
}
if (panels[2].isUnmatched(panels[0], panels[1])) {
panels[2].unmatch();
}
}
ではgogoFlash()
関数を書いていきましょう。
これに仕組みは、フラグが立ってない時と立っている時の画像を2つ用意し、状況に応じて切り替えることにします。
リーチフラグに関する画像をgogoRampという配列にしておきます。そして受け取ったフラグに基づいて処理を実行するように書きます。
const gogoRamp = [ // 画像の配列
'img/gogo_off.jpg',
'img/gogo_on.jpg',
];
const gogo = document.getElementById('gogo'); // エリアの取得
function gogoFlash(e) { // フラグが立つと 1 を持って呼び出される
let gogoRampNum = e; // その値をいったんgogoRampNumに入れる
document.querySelector('#gogo img').src = gogoRamp[gogoRampNum]; // 配列を使ってimgのsrcプロパティを切り替える
}
gogoFlash(0); // 初期値は0にしておく
このようになりました。
ちなみに、777が揃った時にヘッダーパネルも同様にフラグを持って切り替えます。なのでそれも一緒に書いておきましょう。
const headerImage = [
'img/gogo_header01.jpg',
'img/gogo_header02.jpg',
];
const gogoRamp = [
'img/gogo_off.jpg',
'img/gogo_on.jpg',
];
const header = document.getElementsByTagName('header');
function headerFlash(e) {
let headerFlagNum = e;
document.querySelector('header img').src = headerImage[headerFlagNum];
}
const gogo = document.getElementById('gogo');
function gogoFlash(e) {
let gogoRampNum = e;
document.querySelector('#gogo img').src = gogoRamp[gogoRampNum];
}
headerFlash(0);
gogoFlash(0);
これで、何度かゲームを遊ぶとフラグがたったことがわかりやすく表示されると思います。
reachFlagが立っていなかったときの処理
次はreachFlagが立っていないときは、3つ目の画像を止めた際に強制的にチェリーの絵柄になるよう設定し、777を揃えることができないようにしましょう。
それにはcheck777
という関数を使います。やることは前に書いたisUnmatched
関数と同じで、止まった絵柄をチェックして、777なら**(絵柄のurlに'seven'が含まれていたら)**trueというものです。
これもpanelクラス内に書きます。
check777() { // 全部7だったらtrue
return this.img.src.indexOf('seven') !== -1 && panels[1].img.src.indexOf('seven') !== -1 && panels[2].img.src.indexOf('seven') !== -1;
}
次はcheckResult
関数内に追加です。
if (reachFlag === 0) { // reachFlagが立っていなかったら
headerFlash(0);
if (panels[0].check777(panels[1], panels[2])) { // 777が揃った時
panels[2].img.src = 'img/cherry.jpg'; // チェリーを表示させる
}
}
これで仮に狙って7を揃えても、右の絵柄が滑ってチェリーになります。
ちなみにheaderFlash(0);
は、ボーナスゲームなどを終えて再度ゲームを始めた時にリセットできるようにここに書いています。
reachFlagが立っていたときの処理
次は揃う方ですね。これもifを使って条件分岐していきます。
その条件は、「reachFlagが立っている」「check777でtrueが帰ってきてる」です。
これらの条件が揃ったときに行う処理は
・gogoFlash
に0を渡す(リーチフラグ成立表示(ゴーゴーランプ)をオフにする)
・headerFlash
に1を渡す(上部パネルのボーナス成立表示をオンにする)
・reachFlag
に0を代入する(リーチフラグ非成立状態へ移行する)
・get777
に1を代入する(ボーナスゲームを始めるためのフラグを立たせる)
です。これをcheckResult
関数内に書いていきましょう。
if (reachFlag === 1 && panels[0].check777()) { // ボーナスフラグが立ちつつ、777が揃ったら
gogoFlash(0); // リーチフラグ成立表示をオフにする
headerFlash(1); // 上部パネルのボーナス成立表示をオンにする
reachFlag = 0; // リーチフラグ非成立状態へ移行する
get777 = 1; // ボーナスゲームを始めるためのフラグを立たせる
}
これにより上部パネルが変更され、ボーナスゲーム中をアピールしています。
このボーナスゲーム状態(get777 = 1)になると、次のゲームではどこを押してもベルが揃うようにします。
この処理は、ボタンを押すごとに走るので、panelクラス内に書きます。
this.stop.addEventListener('click', () => {
if (this.stop.classList.contains('inactive')) {
return;
}
this.stop.classList.add('inactive');
clearTimeout(this.timeoutId);
if (get777 === 1) { // get777フラグが立っていたら
this.img.src = 'img/bell.jpg'; // 強制的にベルを表示
}
panelsLeft--;
if (panelsLeft === 0) {
gogoNum = Math.floor(Math.random() * 3);
checkResult(gogoNum);
spin.classList.remove('inactive');
panelsLeft = 3;
}
});
そしてこのボーナスゲームが終わる=get777が1のままcheckResult
に進んだらget777を0に戻す処理をcheckResult
関数内に書きます。
if (get777 === 1) { // get777が1だったら
get777 = 0; // get777を0に戻す
}
こうすることで、全てのフラグが0になり、最初と同様にまた遊べるようになりました。
あとがき
以上で完成となります。いかがでしたでしょうか。もともとはドットインストールの講義のものなのですが、勉強も兼ねて思いっきりアレンジしてみました。本当はまだ、リーチフラグがたってなくて777を揃える際、右を最後にしてない場合でも、右側が滑るという問題点があるのですが、かなり長くなりそうなので、一旦ここで終了とさせていただきます。
JavaScriptを収めた方々が見たら、なんでそれ使ってんのとかたくさんあると思いますが、初心者の私にできることで構成してみました。もしこうしたらもっと良くなるよとか、これにはこういう関数の使い方をしたほうがいいよなどありましたらぜひ教えて下さい。
ありがとうございました。
完成版
'use strict';
{
const images = [
'img/seven.jpg',
'img/bell.jpg',
'img/cherry.jpg',
'img/bar.jpg',
'img/grape.jpg',
'img/piero.jpg',
'img/sai.jpg',
];
const headerImage = [
'img/gogo_header01.jpg',
'img/gogo_header02.jpg',
];
const gogoRamp = [
'img/gogo_off.jpg',
'img/gogo_on.jpg',
];
const hitNUm = 0;
let gogoNum = 0;
let reachFlag = 0;
let get777 = 0;
const header = document.getElementsByTagName('header');
function headerFlash(e) {
let headerFlagNum = e;
document.querySelector('header img').src = headerImage[headerFlagNum];
}
const gogo = document.getElementById('gogo');
function gogoFlash(e) {
let gogoRampNum = e;
document.querySelector('#gogo img').src = gogoRamp[gogoRampNum];
}
headerFlash(0);
gogoFlash(0);
class Panel {
constructor() {
const section = document.createElement('section');
section.classList.add('panel');
this.img = document.createElement('img');
this.img.src = this.getRandomImage();
this.timeoutId = undefined;
this.stop = document.createElement('div');
this.stop.textContent = 'STOP';
this.stop.classList.add('stop', 'inactive');
this.stop.addEventListener('click', () => {
if (this.stop.classList.contains('inactive')) {
return;
}
this.stop.classList.add('inactive');
clearTimeout(this.timeoutId);
if (get777 === 1) {
this.img.src = 'img/bell.jpg';
}
panelsLeft--;
if (panelsLeft === 0) {
gogoNum = Math.floor(Math.random() * 3);
checkResult(gogoNum);
spin.classList.remove('inactive');
panelsLeft = 3;
}
});
section.appendChild(this.img);
section.appendChild(this.stop);
const main = document.querySelector('main');
main.appendChild(section);
}
getRandomImage() {
return images[Math.floor(Math.random() * images.length)];
}
spin() {
this.img.src = this.getRandomImage();
this.timeoutId = setTimeout(() => {
this.spin();
}, 600);
}
isMatched(p1, p2) {
return this.img.src === p1.img.src && this.img.src === p2.img.src;
}
isUnmatched(p1, p2) {
return this.img.src !== p1.img.src && this.img.src !== p2.img.src;
}
unmatch() {
this.img.classList.add('unmatched');
}
activate() {
this.img.classList.remove('unmatched');
this.stop.classList.remove('inactive');
}
check777() {
return this.img.src.indexOf('seven') !== -1 && panels[1].img.src.indexOf('seven') !== -1 && panels[2].img.src.indexOf('seven') !== -1;
}
}
function checkResult(gogoNum) {
if (get777 === 1) {
get777 = 0;
}
if (reachFlag === 0) {
headerFlash(0);
if (panels[0].check777(panels[1], panels[2])) {
panels[2].img.src = 'img/cherry.jpg';
}
}
if (hitNUm === gogoNum) {
reachFlag = 1;
gogoFlash(1);
}
if (reachFlag === 1 && panels[0].check777()) {
gogoFlash(0);
headerFlash(1);
reachFlag = 0;
get777 = 1;
}
if (panels[0].isUnmatched(panels[1], panels[2])) {
panels[0].unmatch();
}
if (panels[1].isUnmatched(panels[0], panels[2])) {
panels[1].unmatch();
}
if (panels[2].isUnmatched(panels[0], panels[1])) {
panels[2].unmatch();
}
}
const panels = [
new Panel(),
new Panel(),
new Panel(),
];
let panelsLeft = 3;
const spin = document.getElementById('spin');
spin.addEventListener('click', () => {
if (spin.classList.contains('inactive')) {
return;
}
spin.classList.add('inactive');
panels.forEach(panel => {る
panel.activate();
panel.spin();
});
});
}
body {
background: #bdc3c7;
font-size: 16px;
font-weight: bold;
font-family: Arial, sans-serif;
padding: 0;
margin: 0;
}
# wrapper {
border-left: 10px solid #fff;
border-right: 10px solid #fff;
width: 450px;
text-align: center;
margin: 0 auto;
background-color: #b94047;
padding: 0;
}
main {
width: 300px;
background: #ecf0f1;
padding: 20px;
border: 4px #fff solid;
border-radius: 12px;
margin:16px auto;
display: flex;
justify-content: space-between;
}
header {
width: 350px;
height:100px;
background: #ecf0f1;
border: 4px #fff solid;
margin:0 auto;
text-align: center;
font-size: 2em;
/* background-image: url("../img/gogo_header01.jpg"); */
}
footer {
width: 350px;
height:200px;
margin-top: 20px;
margin:0 auto;
background-image: url("../img/footer01.jpg");
}
.panel img {
width: 90px;
hright: 110px;
margin-bottom: 4px;
}
.stop {
cursor: pointer;
width: 90px;
height: 32px;
background: #ef454a;
box-shadow: 0 4px 0 #d1483e;
border-radius: 16px;
line-height: 32px;
text-align: center;
font-size:14px;
color: #fff;
user-select: none;
}
.flex {
width: 300px;
box-sizing: border-box;
display: flex;
margin: 0 auto;
}
# gogo {
margin:0;
}
.gogo_off {
}
# spin {
cursor: pointer;
width: 200px;
height: 80px;
background: #3498db;
box-shadow: 0 4px 0 #2880b9;
border-radius: 18px;
line-height: 80px;
text-align: center;
color: #fff;
user-select: none;
margin:0 auto 10px 15px;;
}
.unmatched {
opacity: 0.5;
}
.inactive{
opacity: 0.5;
}
.flagarea {}
.flagarea div {
margin: 10px auto;
border: 1px solid #fff;
text-align: center;
padding:15px;
font-size: 20px;
width:250px;
background: #EEE;
}