JS 30 是由加拿大的全端工程师 Wes Bos 免费提供的 JavaScript 简单应用课程,课程主打 No Frameworks
、No Compilers
、No Libraries
、No Boilerplate
在30天的30部教学影片里,建立30个JavaScript的有趣小东西。
另外,Wes Bos 也很无私地在 Github 上公开了所有 JS 30 课程的程序码,有兴趣的话可以去 fork 或下载。
最後一天要实作的内容是"网页版打地鼠"。(下面是实际网页效果的gif图)
<h1>
是页面的标题,旁边的<span class="score">
是打地鼠游戏的记分板。
<button>
在滑鼠点击时,会呼叫startGame()
方法,开始打地鼠游戏。
<div class="game">
是打地鼠游戏的主体,内部有<dvi class="hole">
共计六个洞,每个洞(.hole
)都有一只初始被隐藏的地鼠(<div class="mole">
)。
<h1>Whack-a-mole! <span class="score">0</span></h1>
<button onClick="startGame()">Start!</button>
<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>
初始每一只地鼠(.mole
)都是采用绝对定位(position:absolute
),然後将top
指定为100%,把地鼠(.mole
)隐藏起来。
在游戏过程中,我们会为地洞(.hole
)添加.up
这个 CSS class 选择器,让地鼠探头出来给我们打 XD。
.mole {
/*上略...*/
position: absolute;
top: 100%;
/*下略...*/
}
.hole.up .mole {
top: 0;
}
宣告常数holes
取得所有的地洞(.hole
),资料型态是 NodeList。
宣告常数scoreBoard
取得页面中显示的分数(.score
)。
宣告常数mole
取得所有的地鼠(.mole
),资料型态是 NodeList。
const holes = document.querySelectorAll('.hole'); //NodeList
const scoreBoard = document.querySelector('.score');
const moles = document.querySelectorAll('.mole');
撰写ranTime()
帮我们决定地鼠出现的持续时间并给定参数min
、max
作为出现持续时间范围的最小、最大值。
下面用Math.random()
随机产生一个介於0~1的数字,然後把它乘上(max-min)
再加上min
,最後用Math.round()
四舍五入得到随机的出现持续时间。
function randTime(min,max){
return Math.round(Math.random() * (max-min) + min);
}
randomHole()
帮我们随机选择地鼠出现的洞,为避免接连选到两次一样的洞,所以另外宣告变数lastHole
,来帮我们记住上一次出现的洞。
在方法里,首先要传入所有洞穴(holes
,NodeList),接着一样用Math.random()
随机产生0~1的数字并乘上holes
的长度後,呼叫Math.floor()
无条件舍去小数点,取得一个随机的index
放入常数idx
中。
然後,宣告的常数hole
就可以用这个idx
,随机取得holes
中的一个洞穴。
为避免选到和上次一样的洞穴,利用条件判断hole
是不是跟lastHole
相同,如果相同就递回呼叫randomHole()
,直至选到不同的洞为止。
在方法的最後,把这次的结果放到lastHole
中,之後回传被随机选到的hole
。
let lastHole;
function randomHole(holes){
const idx = Math.floor(Math.random() * holes.length);
const hole = holes[idx]
if(hole === lastHole){
console.log('You got the same hole.');
return randomHole(holes);
}
lastHole = hole;
return hole;
}
宣告变数timeup
作为游戏是否已经结束的flag
。
peep()
是让地鼠从洞穴探头出来的关键!!!
在peep()
的最一开始,宣告常数time
并取得由randTime(200,1000)
随机产生的地鼠持续出现时间,接着宣告常数hole
取得由randomHole(holes)
随机挑出的地洞。
随机挑出的hole
会被添加.up
这个 class,目的是让躲在洞中的地鼠探出头来。
如果超过地鼠持续出现的时间,地鼠就应该要重新回到洞中。所以这边使用setTimeout()
,在经过地鼠出现持续时间time
後,移除hole
上的.up
,让地鼠顺利回家。在游戏未结束(timeup = false
)且前一只地鼠已经回家的状态下,我们会重新呼叫peep()
,让下一只地鼠探头出来。
let timeup = false;
function peep(){
const time = randTime(200,1000);
const hole = randomHole(holes);
hole.classList.add('up');
setTimeout(()=>{
hole.classList.remove('up');
if(!timeup) peep();
},time);
}
宣告变数score
给定初始值0,用来帮我们算分数。
startGame()
可以让我们在点击页面上的button
後,立即开始打地鼠游戏。
在方法的一开始,先把记分板(scoreBoard
)归零,然後把代表结束游戏与否的timeup
设成flase
,同时也把计算的分数(score
)归零。
上面都做完後,呼叫peep()
让地鼠开始探头出来给我们打,接着利用setTimeout()
订定打地鼠游戏的时间限制,这边设定游戏时间为15秒(15000毫秒),15秒後把timeup
设为true
并提示使用者游戏结束。
let score = 0;
function startGame(){
scoreBoard.textContent = 0;
timeup = false;
score = 0;
peep();
setTimeout(()=>{
timeup = true;
alert("时间到,游戏结束!!!");
} , 15000);
}
最後一部分要来处理的是"当地鼠被点击到,页面上的得分要加1,然後地鼠要缩回洞中"。
我们可以为每一只地鼠(mole
)注册click event listener
以bonk()
作为event handler
。
在bonk()
里,首先判断点击是不是"人为"的,如果是使用者点击触发,则e.isTrusted
会回传true
,而如果是用像是script
之类的去触发click event
,则会回传false
并直接停止往下执行方法。
接着,在每一次点击成功後,把分数(score
)加1并移除加到地鼠(mole
)上的.up
,让地鼠回到洞中,之後更新页面上的得分(scoreBoard
)就完成了。
function bonk(e){
if(!e.isTrusted) return; //cheater
score++;
this.classList.remove('up');
scoreBoard.textContent = score;
}
moles.forEach(mole => mole.addEventListener('click',bonk));
Math.random()
Math.round()
Math.floor()
setTimeout()
Element.classList
Node.textContent
Event.isTrusted
>>: [Day 17] Sass - Parent Selector
本节将进行完整的虚拟订单请求发送 def get_order(shop_no, need_pay, ...
模组 在一个 .V 档案里面,可以有很多个 module,但是 Top Module 只会有一个,所...
学习任何东西,都要把基础学的扎实,基础稳了,遇到问题就能迎刃而解。 而学习程序语言的基础就是数学逻...
在上一篇笔记中已经提到 Fork 的功能以及使用办法了,那本篇就来实际发个 Pull request...
今天会接续昨天未讲解的部分往下... Divider 其实他就是分隔线而已,跟 hr tag 是差不...