速记连连看小游戏

1,837 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情

游戏玩法

游戏主要考验玩家的空间感和记忆能力,玩家需要通过开局的3秒内尽可能多的记忆两个空间内相同元素的位置,3秒后将会翻牌把图案盖住,玩家需要点击卡牌来翻转牌面,两张相同图案的卡牌即可保留,游戏计时将在卡牌翻转之后开始,完全翻转所有卡牌则计时结束,我打赌没人能在60秒内通关这个游戏

游戏彩蛋

假如有好事者将鼠标移动到我们的标题看谁翻得快的时候,会发现这是一个复古魂斗罗标题样式,具体实现依赖的是background-image里的repeating-linear-gradient与-webkit-background-clip,我们利用伪元素::before::after ,来实现复古的重叠文字效果。

  • repeating-linear-gradient 重复径向渐变
  • -webkit-background-clip 以盒子内的文字作为裁剪区域向外裁剪,文字之外的区域都将被裁剪掉
<div class="title">看谁翻得快</div>
.title {
  width: 380px;
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  margin: auto;
  display: block;
  color: #1e80ff;
  font-size: 64px;
  letter-spacing: 8px;
  cursor: pointer;
}

  .title::before {
    content: "看谁翻得快";
    position: absolute;
    color: transparent;
    background-image: repeating-linear-gradient(
      45deg,
      transparent 0,
      transparent 2px,
      white 2px,
      white 4px
    );
    -webkit-background-clip: text;
    top: 0px;
    left: 0;
    z-index: -1;
    transition: 1s;
  }

  .title::after {
    content: "看谁翻得快";
    position: absolute;
    color: transparent;
    background-image: repeating-linear-gradient(
      135deg,
      transparent 0,
      transparent 2px,
      white 2px,
      white 4px
    );
    -webkit-background-clip: text;
    top: 0px;
    left: 0px;
    transition: 1s;
  }

  .title:hover:before {
    top: 10px;
    left: 10px;
  }

  .title:hover:after {
    top: -10px;
    left: -10px;
  } 

核心实现

  • 容器 容器是由720x450的盒子拆成的两个350x450的小盒子构成,大盒子通过flex布局水平垂直居中,小盒子也是通过flex布局实现水平拆分。
<div id="box">
    <div class="left"></div>
    <div class="right"></div>
  </div>
#box{
    width: 720px;
    height: 450px;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .left,.right{
    width: 350px;
    height: 450px;
    background: #fff;
    border-radius: 8px;
    overflow: hidden;
    display: flex;
    justify-content: space-around;
    align-items: center;
    flex-wrap: wrap;
    transform-style: preserve-3d;
  }
  • 元素,元素样式是100x100的小卡片,背景图随机生成,背景图来源于掘金商城,随机生成算法还是通过标记法去重,图片一共12张,所以我们需要生成不重复的随机顺序的12个数字,从0开始。将生成的数字以 自定义属性key记录在对应的dom上, 牌面的翻转覆盖效果通过transition配合transform来实现,不管是添加还是移除,都会应用过渡效果,所以看起来像是在翻转一样
// 样式
.left div, .right div{
    width: 100px;
    height: 100px;
    border-radius: 8px;
    cursor: pointer;
    box-shadow: inset 0 0 15px 3px rgb(12, 133, 215);
    background-position: center;
    background-size: contain;
    background-repeat: no-repeat;
    transform: rotateY(180deg);
    transition: all ease .5s;
  }
  #box .shine{
    transform: rotateZ(360deg) rotateY(360deg);
  }
  // 随机算法
  // 参数map是标记对象,from是为了将dom正确append, imgSrc比较多,不在这里罗列了
  function randomKey (map, from) {
      let ket = null
      do {
        key = Math.floor(Math.random()*len)
      } while (map[key])
      let div = $('<div>')
      div.addClass('item')
      div.addClass('shine')
      div.attr('key', key)
      div.css('background-image', `url(${imgSrc[key]})`)
      map[key] = div
      $(`.${from}`).append(div)
    }
    for(let i =0;i<imgSrc.length;i++) {
      randomKey(left_Map, 'left')
      randomKey(right_Map, 'right')
    }
  • 倒计时3s翻转卡牌 ,这里采用了定时器的方法,上面讲过,添加移除transfrom都会响应过渡,那么我们只需要在倒计时结束时统一为卡牌更换背景图即可
let t =setTimeout(function () {
      for(let i in left_Map) {
        left_Map[i].removeClass('shine')
        left_Map[i].css('background-image', `url(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0a4ce25d48b8405cbf5444b6195928d4~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp)`)
      }
      for(let i in right_Map) {
        right_Map[i].removeClass('shine')
        right_Map[i].css('background-image', `url(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0a4ce25d48b8405cbf5444b6195928d4~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp)`)
      }
  },3000)
  • 添加点击事件,处理翻牌相同和翻牌不同的情况, 这里,我们需要一个缓存数组cache,将点击获得的属性key缓存在cache里,然后进行判断即可,这里我们只需要判断,cache有长度和cache没长度的情况,有长度则需要进行比较,没长度直接将key缓存在cache内即可,相关注释都在代码里了
 $('.item').click(function () {
 // 这里判断的是 假如已经翻转了牌面,就不再响应点击事件
        if ($(this).hasClass('shine')) {
          return false
        }
        // 获取key,将牌面翻转并设置key对应的背景图
        let key = Number($(this).attr('key'))
        $(this).addClass('shine')
        $(this).css('background-image', `url(${imgSrc[key]})`)
        // 判断缓存数组长度
        if (cache.length === 1) {
        // 判断当前点击Key与缓存的key是否一致
          if (cache.find(v => v===key) !== undefined) {
            cache = []
            // 更新成功翻牌配对次数
            findNum++
            console.log(findNum)
            // 判断翻牌结束条件,停止计时
            if (findNum>=12) {
              clearInterval(timer)
              $('#time').text('游戏结束,用时'+timeText+'秒')
            }
            return false
          } else {
             // 加一个人为延时,处理已经翻转但配对不成功的两个牌面,并清空cache缓存
            let self = this
            let inx = cache[0]
            cache = []
            let tm = setTimeout(function () {
              $(`.item[key=${inx}]`).removeClass('shine')
              $(`.item[key=${inx}]`).css('background-image', `url(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0a4ce25d48b8405cbf5444b6195928d4~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp)`)
              $(self).removeClass('shine')
              $(self).css('background-image', `url(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0a4ce25d48b8405cbf5444b6195928d4~tplv-k3u1fbpfcp-no-mark:0:0:0:0.awebp)`)
              
            }, 500)
          }
        } else {
           // 没长度直接记录缓存
          cache.push(key)
        }

      })

结语 & 码上掘金地址

笔者多次尝试,哪怕作弊,时间也在110秒左右,真的很考验记忆力