終於來到 JS30的最後一天,在今天的課題中,我們將要使用一些之前所學的技能,在頁面上做出一個打地鼠的小遊戲。[1]
實作連結
要做出一個打地鼠小遊戲,我們先列出需要的功能:
可以看到我們的地洞與地鼠結構如下,在六個地洞父容器中,分別包了六個地鼠子元素,而首先我們先把會需要用到的元素物件都選取起來:
<div class="game">
<div class="hole hole1">
<div class="mole"></div>
</div>
<div class="hole hole2">
<div class="mole"></div>
</div>
<div class="hole hole3">
<div class="mole"></div>
</div>
<div class="hole hole4">
<div class="mole"></div>
</div>
<div class="hole hole5">
<div class="mole"></div>
</div>
<div class="hole hole6">
<div class="mole"></div>
</div>
</div>
const holes = document.querySelectorAll('.hole');
const scoreBoard = document.querySelector('.score');
const moles = document.querySelectorAll('.mole');
要取得隨機的數字我們可以使用 Math.random()
加上 Math.floor()
這兩個方法。
Math.random()
方法,會隨機回傳一個大於等於0、小於1的浮點數。[2]Math.floor()
方法,會回傳小於輸入參數的最大整數。[3]將以上兩個方法結合起來,就可以取得一定範圍內的隨機數值。假如我們想要取得 X 內的隨機數值:
let randomNumer = Math.floor(Math.random() * (X+1));
將以上的方法組合起來,我們可以執行一個函式,讓他執行之後回傳一個有上下限的隨機時間:
function randomTime(min, max) {
let time = Math.floor(Math.random() * (max - min) + min);
return time
};
再來要隨機取得地洞位置。透過前面 document.querySelectorAll('.hole')
方法,我們取得一個包含所有 <div class="hole">
元素的 NodeList
類陣列物件,而這個類陣列可以使用一些陣列方法,因此我們可以使用 NodeList[index]
方法來取得我們想取得的物件。如此一來,我們就可以透過產生隨機數字的方法,隨機取得我們我們選取的元素之一。
而為了不要重複選到相同的元素,我們給了一個代表上一次出現元素的變數,當如果這次選到的元素與上次相同,就再選取一次:
let lastHole;
function randomHoles() {
//我們總共有6個元素,因此 index 為0~1
let index = Math.floor(Math.random() * 6);
//取的隨機一個 <div class="hole"> 元素
let currentHole = holes[index];
//與上次相同就再執行一次隨機選元素的函式
if (currentHole === lastHole) {
return randomHoles();
//不同的話就把這次的結果指派給變數,並回傳這次選到的元素
} else {
lastHole = currentHole;
return currentHole;
};
};
要讓地鼠元素上升與下降,最簡單的方式就是改變其位置,而在作者給的草稿中,我們看到代表地鼠的元素 <div class="mole"></div>
的 CSS 屬性中,使用了 position:absolute
以及 top:100%
這兩個屬性,並且在父容器中給了 overflow:hidden
這個 CSS 屬性,因此剛開始的時候,地鼠並不會出現在頁面上。
知道這些設定之後,我們就可以透過更動改變地鼠元素的 top
屬性,來讓他出現在頁面上。這邊使用前面常常使用的新增 up
這個 class,來更動元素的 CSS 屬性為 top:0
,如此一來就能讓地鼠鑽出地洞。
而當指定的時間一到,地鼠就要回到原本的位置消失不見。這邊可以使用 setTimeout()
這個方法,在指定的時間到了之後,就將原本的元素移除 up
這個 class,地鼠就會鑽回地下。
接著要讓地鼠一隻接著一隻出現在頁面上。當上一隻地鼠時間到執行 setTimeout()
這個方法,我們就要再執行一次出現地鼠的函示,讓下一隻地鼠鑽出地洞。而這邊設定了一個開關的判斷式,當停止開關為 false
時,會再繼續出現下一隻地鼠。而當開關被打開時,就停止出現下一隻地鼠,並把開關的狀態重置:
function popUp() {
//取得地鼠在頁面上的時間,單位為毫秒
let time = randomTime(500, 900);
//取得地鼠要出現的地洞元素
let hole = randomHoles();
//將該地洞加上 up 這個 class 讓地鼠出現
hole.classList.add('up');
//顯示在頁面上的時間到之後執行以下程式碼
setTimeout(function () {
//將取得的元素移除 up 這個 class 讓地鼠下降
hole.classList.remove('up');
if (turnOff === false) {
popUp();
} else {
turnOff = false;
}
}, time);
};
.hole.up .mole {
top:0;
}
打到出現的地鼠的事件,可以透過加上監聽事件 onclick
來執行,在觸發的函式當中,我們需要做到兩件事情:
加分的動作,我們可以先給一個代表分數的變數,當每次點擊到地鼠的時候,該分數就加一,並且將新的分數指定為代表計分板元素的 innerHTML
。
讓地鼠下降的功能,只要在觸發事件的時候,移除該子元素容器的父元素 up
class,就能達成。
而這邊作者特別使用了 event.isTrusted
屬性,來避免作弊的發生。這個屬性代表該事件,是否為 trusted
。而每個瀏覽器對於事件是否為 trusted
有不同的規定,在 Chrome、Firefox 以及 Opera 瀏覽器中,當事件是藉由使用者觸發而不是其他程式碼,此事件的 isTrusted
屬性就為 true
。[4]
moles.forEach(function (mole) {
mole.addEventListener('click', score);
});
let currentScore = 0;
function score(event) {
if(!event.isTrusted) return;
currentScore++;
scoreBoard.innerHTML = currentScore;
this.parentNode.classList.remove('up');
};
最後我們要啟動遊戲與終止遊戲,啟動遊戲的方法很簡單,只要按下我們的啟動按鈕,就執行讓地鼠出現在頁面上的函式,地鼠就會陸續出現在頁面上。並且將分數歸零之後,重新印在畫面上。
而要如何讓遊戲終止呢?在開始遊戲的函式中,加入一個 setTimeout()
方法,讓遊戲時間到了之後,就將代表開關的參數改為 true
,如此一來當 popUp()
函式發現參數的布林值已被修改,就會停止出現下一隻地鼠:
let turnOff = false;
function startGame() {
currentScore = 0;
scoreBoard.innerHTML = currentScore;
popUp();
setTimeout(function () {
turnOff = true;
}, 10000);
};
完成以上的程式碼,就能做出打地鼠小遊戲囉!!
在今天的課題中,我們結合了 CSS 與 Javascript ,做出了一個打地鼠小遊戲。
在這三十天的過程當中,我們認識了許多常用的 Javascript 方法、事件與屬性,做出了許多不同功能的程式碼,並結合了許多不同的 CSS 屬性,創造出了許多酷炫的網頁特效。雖然 Javascript 與 CSS 都還有許多沒碰過的東西,但是相信這三十天下來,我們已經掌握基本的觀念與技巧,讓我們在前端工程師這條道路上,已經具備相當的基礎能力。
這三十天下來,對於我自己來說不只是單純的做完 JS30。藉由要寫成文章給別人看的壓力,強迫自己去理解每個使用到的東西,這些時間下來,也發現這些東西也成為屬於自己的技能。或許這些東西都非常的基礎,但是我深刻感受到壓力會使人快速成長的概念XD
感謝主辦單位 iThome 挖了這個坑,讓我有被推進去的機會。
感謝好想工作室的Howard推坑,讓我這個新手也可以達成這個里程碑。
感謝好想工作室的夥伴們,讓我可以一直保持這股動力,雖然是以團報的方式互相傷害,但也因為我們的團結,才讓我們一起完成鐵人三十的比賽!
最後感謝不知名的訂閱觀眾們,讓我這個新手消除了我寫的怎麼會有人看的念頭。
沒梗的時候只能套句老掉牙的 slogan:要感謝的人太多了,那就謝天吧!!