最近一次更新修复老鼠自动隐藏时候点击得分,造成多个老鼠出现的bug
纯原生打地鼠Demo一个多小时盲掰出来,绝对100%纯原生,不含任何第三方杂质。
预览注意你的钛合金X眼!
对不起,辣到你的眼睛了……
但,这真的是个打地鼠!!!
源码结构没结构,就一个index.html搞定,没有JQ,没有其他,纯纯纯原生。
源码解析一个文件,我们从html到css再到js一点点解析。
HTML
代码侍候:
<header>
<span id="score">得分:0</span>
<button ="setLevel()">难度</button>
<button ="restart()">重玩</button>
</header>
<main>
<section class="mouse-ground">
<div class="hole"></div>
<button class="mouse"></button>
<div class="grass"></div>
</section>
<div class="mouse-ground">
<div class="hole"></div>
<button class="mouse"></button>
<div class="grass"></div>
</div>
<div class="mouse-ground">
<div class="hole"></div>
<button class="mouse"></button>
<div class="grass"></div>
</div>
<div class="mouse-ground">
<div class="hole"></div>
<button class="mouse"></button>
<div class="grass"></div>
</div>
<div class="mouse-ground">
<div class="hole"></div>
<button class="mouse"></button>
<div class="grass"></div>
</div>
<div class="mouse-ground">
<div class="hole"></div>
<button class="mouse"></button>
<div class="grass"></div>
</div>
<div class="mouse-ground">
<div class="hole"></div>
<button class="mouse"></button>
<div class="grass"></div>
</div>
<div class="mouse-ground">
<div class="hole"></div>
<button class="mouse"></button>
<div class="grass"></div>
</div>
<div class="mouse-ground">
<div class="hole"></div>
<button class="mouse"></button>
<div class="grass"></div>
</div>
</main>
<footer>一些说明</footer>
结构清晰,header->main->footer,三段式。
header展示得分,重玩,以及难度。其中难度是后面做完了初版突然想起来的,使用的是prompt获取处理。
footer没什么内容,可以用来写点版权什么的,甚至去掉都行。
主要说一下main:
一共9个老鼠洞,3*3结构,并排处理,为了各位的眼睛,我还是找能看的图替换一下(其实是为了具象化,不然小伙伴可能不懂为什么是这种结构)。单个的老鼠洞如下:
解释一下为什么代码里面是hole, mouse, grass,结构,事实上,这三个缺一不可。
hole洞好理解,mouse也是必须的,这两个没问题。咋一看好像grass不是必须的?NONONO,太年轻了小伙子。
你不想想,如果只有洞和老鼠,那动画就是这样的:
是不是很尴尬,理所应当嘛,洞在下面,老鼠在上面,移动老鼠的效果自然这样,你脑子里想想换成可爱的老鼠和比较真实的洞,什么感觉……
所以用一个grass,当然你可以取别的名字,我是觉得草皮好看点吧,如果有的话……覆盖在老鼠上面就可以挡住老鼠,造成老鼠缩到洞里的假象。
那你一定会问,打地鼠游戏可没见过这样一个挡住老鼠的东西啊?
所以说,你太年轻,看下图所示:
你看到的一个洞,上图上面那个,其实是由上图下面两个组合而成的,结构如下:
后面那个洞我没切割,因为对于我这个项目意义不大,如果是图片就有必要了,因为图片比较占用资源。
看懂没?
懂了的话,我们就去CSS吧。
--
CSS
代码伺候:
html, body {
height: 100%;
margin: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
body {
width: 320px;
}
header, main, footer {
display: flex;
width: 100%;
height: 60px;
}
header {
justify-content: space-between;
align-items: center;
background: yellow;
}
main {
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
height: 320px;
background: green;
}
footer {
justify-content: center;
align-items: center;
background: red;
}
.mouse-ground {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 84px;
height: 84px;
background: blue;
}
.hole {
width: 60px;
height: 60px;
/* 椭圆代办 */
/* border-radius: 15px/30px 30px/15px; */
border-radius: 30px;
background: black;
}
.mouse {
position: absolute;
left: 22px;
top: 44px;
width: 40px;
height: 40px;
background: purple;
transition: transform 1s linear;
transform: translateY(0);
cursor: pointer;
}
.grass {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 40px;
background: greenyellow;
opacity: .5;
}
我没有刻意去做适配,所以最猥琐的使用了320像素的宽度,保证手机刻意正常展示(嘿嘿嘿)。
常规内容不做解释,还是说说老鼠洞那里。
老鼠洞(mouse-ground)整个里面包含三个,hole,mouse与grass,理由上面已经说了,这里主要是处理层级关系。
最方便的当然是使用position处理,因为已经设置宽度320像素,所以数值就非常好计算,具体见上面。
老鼠洞是基准,所以需要relative,真正的洞可以居中,最方便的是设置老鼠洞为flex,各种轴居中即可:justify-content: cneter; align-items: center;
然后mouse与grass设置absolute,即可完美呈现在上方,位置调一调就可以做到想要的效果了。
这种结合js逻辑的动画,能用transition就用transition,比keyframes好控制的多。
然后由于有位置变化的动画,所以动transform吧,这个最省资源:transition: transform 1s linear;
,这个由于后面有难度控制,所以设置写在了js里面,为了是js可以控制其中动画时间。当然,css里面也可以先写,再改,不过个人不喜欢,所以就没写。
看懂了?懂了就进js了啊!
JS
废话不多说,直接成品代码:
/**
* 一共9只老鼠,得弄个随机数来提供让老鼠出来,现在老鼠都是缩在洞里。
* 一只出来,等一秒钟,没有点击就回去,回去后,另一只再出来。
*/
const gameConfig = {
level: 5, // 1-9等级
}
let totalScore = 0
// 拿到所有的老鼠
const mouses = document.querySelectorAll('.mouse')
// 绑定出现时候的2s自动隐藏事件,避免击打造成提前隐藏后,出现内部那个2s隐藏再次触发。
let timeout
// 击打
const hit = e => {
// 加分
score.innerText = `得分:${++totalScore}`
const mouse = e.target
// 击打后,避免多次击打
mouse.disabled = true
// 隐藏老鼠
hidden(mouse)
// 击打了,就取消出现内部2s自动隐藏事件
clearTimeout(timeout)
}
// 每只老鼠绑定击打事件
mouses.forEach(mouse => mouse.addEventListener('click', hit))
// 出来
const show = mouse => {
mouse.style.transform = 'translateY(-40px)'
// 出来一秒后自动缩回去,出现花1s,等待1s,所以要2s
timeout = setTimeout(() => hidden(mouse), 2 * (1000 - gameConfig.level * 100))
}
// 隐藏
const hidden = mouse => {
mouse.style.transform = 'translateY(0)'
// 隐藏后,还得出新的老鼠。
setTimeout(() => {
// 隐藏后,解开可能的disabled
mouse.disabled = false
show(getRandomMouse())
}, 1 * (1000 - gameConfig.level * 100))
}
// 随机获取老鼠
const getRandomMouse = () => mouses[~~(Math.random() * 9)]
// 重新开始游戏
const restart = () => {
// 还原所有,分、动画
// 还原分
score.innerText = '得分:0'
// 所有的都缩回去
hiddenAll()
// 清除遗留的出现内部绑定的事情
clearTimeout(timeout)
// 开始
setTimeout(() => show(getRandomMouse()), 1 * (1000 - gameConfig.level * 100))
}
const hiddenAll = () => mouses.forEach(mouse => {
mouse.style.transform = 'translateY(0)'
// 设置难度时候用到
mouse.style.transition = `transform ${1 - gameConfig.level / 10}s linear`
clearTimeout(timeout)
})
// 设置难度
const setLevel = () => {
const value = prompt('设置游戏难度,请输入1-9。别手贱,会鬼畜。。。')
const warning = () => {
alert('请输入有效值')
setLevel()
}
switch(value) {
case null: console.log("点击了取消")
break
case "": warning()
break
default: {
if(Number(value)) {
gameConfig.level = Number(value)
hiddenAll()
setTimeout(restart, 1000)
}
else warning()
}
}
}
restart()
忽然觉得js好像最不用说,又好像值得大说特说……
代码有大量的注释,下方不再过多解释,不懂或有疑问请留言。
注意,尽管本项目非常羞涩,但还是个游戏对不,所以作为一个游戏,最起码的几个逻辑又要:
-
游戏开始(相当于重新开始)
-
游戏结束(本游戏没有扣分机制,所以暂无结束)
- 业务逻辑(打地鼠、隐藏地鼠、显示地鼠、地鼠停留等)
这些js里面都有注释,可以仔细看看源码。
这里注意代码里面大量的异步逻辑,以及可能你完全不熟悉的业务逻辑,甚至是不熟悉的业务bug(比如原本定时隐藏,但是由于击打造成提前隐藏,从而后面一系列的坑爹逻辑bug……)。
如果你有心学习,建议clone我的源码,然后从最初版本,一个版本一个版本的看过来,我把版本划分的还是颗粒比较细的,有问题请直接留言下方即可。
游戏在线地址:纯原生DOM版打地鼠
最后,祝大家玩的开心。
源码:GitHub
喜欢的话请关注一波,定期更新技术文章,满满的都是干货。