iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 30
0

Day30-課題內容

終於來到 JS30的最後一天,在今天的課題中,我們將要使用一些之前所學的技能,在頁面上做出一個打地鼠的小遊戲。[1]
實作連結

進入課題

要做出一個打地鼠小遊戲,我們先列出需要的功能:

  1. 地鼠於隨機位置出現且持續時間長度不一。
  2. 地鼠於位置上升後下降
  3. 打到地鼠的事件
  4. 啟動遊戲與終止遊戲

可以看到我們的地洞與地鼠結構如下,在六個地洞父容器中,分別包了六個地鼠子元素,而首先我們先把會需要用到的元素物件都選取起來:

<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() 這兩個方法。

  1. Math.random() 方法,會隨機回傳一個大於等於0、小於1的浮點數。[2]
  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 來執行,在觸發的函式當中,我們需要做到兩件事情:

  1. 加分
  2. 地鼠下降

加分的動作,我們可以先給一個代表分數的變數,當每次點擊到地鼠的時候,該分數就加一,並且將新的分數指定為代表計分板元素的 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:要感謝的人太多了,那就謝天吧!!

參考資料

  1. javascript30
  2. MDN-Math.random()
  3. MDN-Math.floor()
  4. w3school-isTrusted Event

上一篇
JS30-Day29-Countdown Timer
系列文
新手也能懂的JS3030

尚未有邦友留言

立即登入留言