zweizhao.github.io

项目,文章,随笔,博客,Markdown,个人网站,人生感悟。

View on GitHub

纯原生打地鼠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

是不是很尴尬,理所应当嘛,洞在下面,老鼠在上面,移动老鼠的效果自然这样,你脑子里想想换成可爱的老鼠和比较真实的洞,什么感觉……

所以用一个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好像最不用说,又好像值得大说特说……

代码有大量的注释,下方不再过多解释,不懂或有疑问请留言。

注意,尽管本项目非常羞涩,但还是个游戏对不,所以作为一个游戏,最起码的几个逻辑又要:

  1. 游戏开始(相当于重新开始)

  2. 游戏结束(本游戏没有扣分机制,所以暂无结束)

  3. 业务逻辑(打地鼠、隐藏地鼠、显示地鼠、地鼠停留等)

这些js里面都有注释,可以仔细看看源码。

这里注意代码里面大量的异步逻辑,以及可能你完全不熟悉的业务逻辑,甚至是不熟悉的业务bug(比如原本定时隐藏,但是由于击打造成提前隐藏,从而后面一系列的坑爹逻辑bug……)。

如果你有心学习,建议clone我的源码,然后从最初版本,一个版本一个版本的看过来,我把版本划分的还是颗粒比较细的,有问题请直接留言下方即可。

游戏在线地址:纯原生DOM版打地鼠

最后,祝大家玩的开心。


源码:GitHub


喜欢的话请关注一波,定期更新技术文章,满满的都是干货。