Day30 Whack A Mole!

Demo

終於來到最後一天
透過前面29累積下來的經驗來做一個打地鼠吧

HTML/CSS結構

1
2
3
<div class="hole hole1">
<div class="mole"></div>
</div>

我們有地鼠的洞hole和地鼠mole
而CSS的部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.hole {
flex: 1 0 33.33%;
overflow: hidden;
position: relative;
}
.mole {
background:url('gophers.png') bottom center no-repeat;
background-size:80%;
position: absolute;
top: 100%;
width: 100%;
height: 100%;
transition:all 0.4s;
}
.hole.up .mole {
top:0%;
}

原始範例透過top:100%以及父層的overflow:hidden讓地鼠隱藏
.hole加了.up之後,.mole就會跑上來

哪些事情要做

對象:

  1. 按鈕(<button onClick="startGame()">Start!</button>)
  2. 每隻地鼠做事件偵聽
    1
    moles.forEach((mole) => mole.addEventListener('click', hit))

涵式:

  1. 按下按鈕時: startGame()
  2. 哪個洞: getHole()
  3. 多久要回去 : getTime()
  4. 跑出來/回去 : show()
  5. 打擊: hit()

開始遊戲

既然有開始遊戲就有結束的時候,所以我們定義timpUp判斷時間是否結束
還有分數score一開始為0分;而地鼠的出現有先後之分,我們為了避免地鼠同地點重複出現我們要去判斷 現在這隻剛剛那隻 有沒有一樣所以定義一個last待會會用到。

1
2
3
4
5
6
7
8
9
10
11
12
13
let timpUp = false
let score = 0
let last

function startGame() {
timeUp = false
score = 0
scoreBoard.textContent = score
show()
setTimeout(() => {
timeUp = true
}, 10000) //遊戲時間10秒
}

開始遊戲,將timpUp設為false,這邊我們定義false代表時間還沒到
再透過setTimeout設定10秒後將timpUp設為true代表時間到。

地鼠出現

1
2
3
4
5
6
7
8
9
function show() {
const time = getTime()
const hole = getHole()
hole.classList.add('up')
setTimeout(() => {
hole.classList.remove('up')
if (!timeUp) show();//時間還沒到繼續做
}, time);
}

出現與消失的方法簡單透過classList.addclassList.remove即可
這邊的關鍵在於哪個位置的地鼠,和出現多久(多久後要remove class),所以我們交給 getTime()getHole() 幫我們取得隨機位置和時間。

哪個位置的地鼠

1
2
3
4
5
6
7
8
9
10
11
function getHole() {
const now = Math.floor(Math.random() * 5) //四捨五入
const hole = holes[now]
if (hole === last) {// 如果下一個等於上一個(重複)就再取一次
console.log('重複');
return getHole()
}
last = hole
console.log(now)
return hole
}

我們有6個洞,序列為0~5,所以我們取隨機數0~5
我們就取到這個隨機位置了const hole = holes[now]

回傳給show()函數前,我們做個小判斷,避免同一位置出現地鼠,
所以last就派上用場了,讓last暫時等於現在取得的這個值,
而下次執行時就會判斷是否重複,如果重複就再做一次getHole(),依此類推。

出現多久

1
2
3
function getTime() {//自訂時間:0.5s(500)~1s(1000)間
return Math.floor(Math.random() * 1000 + 500)
}

可以自訂要出現多久,這邊我設定為0.5~1秒之間
這邊完成後就可以按下開始看到地鼠隨機跑出來了~

打擊

1
2
3
4
5
6
7
8
9
10
11
function hit() {
score++
scoreBoard.textContent = score;
this.parentNode.classList.remove('up')//地鼠的上層,控制地鼠的DOM
//console.log(this.parentNode)
this.style.pointerEvents = 'none'//暫時讓地鼠不可再被點擊

setTimeout(() => {//0.5秒後才可再被點擊
this.style.pointerEvents = 'visible'
}, 500)
}

每打擊一次score就+1並顯示出來
而這邊有個小關鍵是,由於我們的this是地鼠,
打擊後要remove class的並不是地鼠(this),
而是他的父層this.parentNode.classList.remove('up')

除此之外,在這邊我為了防止一隻地鼠在短時間內可能被重複點擊
我們可以在點擊後立刻加上css屬性pointer-events:'none'
並在0.5秒後再讓這隻地鼠屬性改回pointer-events:'visible'

自己加上槌子

1
2
3
<div class="game">
<div class="hammer"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
.hammer{
background: url('hammer.png');
background-size: contain;
position: absolute;
width: 160px;
height: 160px;
top: 0;
left: 0;
z-index: 100;
pointer-events: none;
}

我們把槌子放在<div class="game"></div>裡面
(不放在外面(<body>)是怕擋到其他元素)並設定css

接著我們將事件設定在game上面

1
2
3
4
5
6
7
8
const game = document.querySelector('.game')
const hammer = document.querySelector('.hammer')
function handler(e) {
const mousePos = {x: e.pageX,y: e.pageY}
hammer.style.top = mousePos.y-120 + 'px'
hammer.style.left = mousePos.x-80 + 'px'
}
game.addEventListener('mousemove', handler)

我們取得滑鼠位置後再賦予到槌子身上(再依需求修飾滑鼠位置的值)
最後在簡單加上槌子轉動的樣式

1
2
3
4
5
6
document.addEventListener('mousedown', () => {
hammer.style.transform = 'rotate(-45deg)'
})
document.addEventListener('mouseup', () => {
hammer.style.transform = 'rotate(0deg)'
})

就完成槌子的部分囉!

程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
const holes = document.querySelectorAll('.hole');
const scoreBoard = document.querySelector('.score');
const moles = document.querySelectorAll('.mole');
const game = document.querySelector('.game')
const hammer = document.querySelector('.hammer')
let timpUp = false
let score = 0
let last;//最後一個出現的

function startGame() {
timeUp = false
score = 0
scoreBoard.textContent = score
show()
setTimeout(() => {
timeUp = true
}, 10000) //遊戲時間10秒
}

function getTime() {//自訂時間:0.5s(500)~1s(1000)間
return Math.floor(Math.random() * 1000 + 500)
}

function getHole() {//0~5的洞
const now = Math.floor(Math.random() * 5) //四捨五入
const hole = holes[now]
if (hole === last) {// 如果下一個等於上一個(重複)就再取一次
console.log('重複');
return getHole()
}
last = hole
return hole
}

function show() {
const time = getTime()
const hole = getHole()
hole.classList.add('up')
setTimeout(() => {
hole.classList.remove('up')
if (!timeUp) show();//時間還沒到繼續做
}, time);
}

function hit() {
score++
scoreBoard.textContent = score;
this.parentNode.classList.remove('up')//地鼠的上層,控制地鼠的DOM
this.style.pointerEvents = 'none'//暫時讓地鼠不可再被點擊
setTimeout(() => {//0.5秒後才可再被點擊
this.style.pointerEvents = 'visible'
}, 500)
}

function handler(e) {
const mousePos = {x: e.pageX,y: e.pageY}
hammer.style.top = mousePos.y-120 + 'px'
hammer.style.left = mousePos.x-80 + 'px'
}

moles.forEach((mole) => mole.addEventListener('click', hit))
game.addEventListener('mousemove', handler)
document.addEventListener('mousedown', () => {
hammer.style.transform = 'rotate(-45deg)'
})
document.addEventListener('mouseup', () => {
hammer.style.transform = 'rotate(0deg)'
})
0%