基于Jquery实现一个连连看小游戏,我赌你连普通级也过不去

891 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情 >>

前言

日更挑战接近尾声,本就不富裕的才思更是日渐枯竭,故写一写小游戏来完成任务,连连看的游戏规则是玩家将2个相同图案连接起来,第一次使用鼠标点击牌池里的图案,该图案此时为“被选中”,图案四周有光环围住,再用鼠标点击你所看到的另一个相同图案,并把这两个图案连起来,而且中间的连线不能超过三根,这样就可以消掉这两个相同的图案。

实现

牌面容器实现

这个很简单,一张背景图,一个水平垂直居中的div,然后flex布局即可。

 <div
    class="box"
    id="box"
  >

  </div>
  <style>
  * {
    margin: 0;
    padding: 0;
    list-style: none;
    box-sizing: border-box;
  }

  html,
  body {
    width: 100%;
    height: 100%;
    overflow: auto;
    background-image: url('./bg1.jpeg');
    background-size: cover;
  }

  .box {
    width: 800px;
    height: 800px;
    display: flex;
    justify-content: flex-start;
    align-content: flex-start;
    flex-wrap: wrap;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto;
    transform-style: preserve-3d;
  }

  .box .item {
    border-radius: 8px;
    background: #fff;
    border: 1px solid #f4f4f4;
    cursor: pointer;
    color: #333;
    font-size: 20px;
    text-align: center;
    font-weight: 600;
    transition: all ease .5s;
  }
</style>

生成连连看图片

这里我们需要考虑到几个事情

  • 需要同时生成两个相同元素的div,用来进行图案索引
  • 生成dom的数组要进行乱序处理 下面的代码中 level是为了增加挑战性的,我们先忽略不计。 下面的代码中 div的数量是50 x level x 2 所以我们在计算宽高的时候,因为是要生成正方形,所以相当于是在求 总面积一定,元素个数一定的情况下,开平方的值作为元素宽高
$(function () {
    let level = 1
    let map = {}
    let arr = []
    function initLLKan() {
      $('#box').empty()
      map = {}
      arr = []
      for (let i = 0; i < 50*level; i++) {
        let x = y =  Math.floor(Math.random() * 50*level + 1)
        arr.push({ x, y })
        arr.push({ x, y })
      }
      arr = randomArr(arr)
      arr.map(v => {
        let div = $('<div>')
          div.addClass('item')
          div.css('width', Math.sqrt((800*800)/(100*level)) + 'px')
          div.css('height', Math.sqrt((800*800)/(100*level)) + 'px')
          div.css('line-height', Math.sqrt((800*800)/(100*level)) + 'px')
          div.css('font-size', Math.sqrt((800*800)/(600*level)) + 'px')
          div.attr('x', v.x)
          div.attr('y', v.y)
          div.html('?')
        $('#box').append(div)
      })
     
    function randomArr (array) {
      let arr = [].concat(array)
      for (let i = arr.length-1;i>=0;i--) {
        let inx = Math.floor(Math.random()*i+1)
        let next = arr[inx]
        arr[inx] = arr[i]
        arr[i] = next
      }
      return arr
    }
    initLLKan()

注意看上文代码中的乱序数组的函数randomArr 这里看着是不是很眼熟?这不就是类似冒泡排序的写法吗?只不过我们是进行了单次随机直接换位置了,这样实现的随机是相对真随机乱序,还有一种实现是伪随机乱序,就是通过数组的sort方法进行排序时,条件里通过.5-Math.ceil(Math.random()*0.5)进行干扰,从而返回有时负数有时正数的值进行伪随机乱序。

我们通过遍历的方式将元素append进我们的容器内,并对元素绑定x属性,x属性就是我们的匹配标识

添加选中的逻辑

这里我们采用的是非常经典的事件代理的方式进行的,这样做的好处是,因为上文中针对容器的push操作是异步的,如果我们给div直接添加事件造成的结果是有可能jquery选择不到我们想要的元素。所以这里我们采用事件代理的方式进行处理,而且对每个div进行绑定事件和通过事件代理的方式绑定事件显然性能上是事件代理更为合理的,利用的就是事件的冒泡特性,这里有几个情况需要判断

  • 需要判断触发事件的元素e.target是我们的item元素触发的
  • 需要判断触发事件的元素e.target是还没选中两个的元素(未匹配)
  • 需要判断假如添加的类名transform大于两个的时候,要将所有已添加的transform类名移除
  • 需要判断当前已经添加transform类名的元素x属性是否一致,假如一致,就添加binGo类名移除transform类名,注意这里的顺序是不能颠倒的
  • 需要判断游戏结束的条件就是binGo类名的元素数组长度等于我们item类名的元素数组长度即为获胜
 $('#box').on('click', function (e) {

        if ($(e.target).hasClass('item') && !$(e.target).hasClass('binGo')) {
          $(e.target).html($(e.target).attr('x'))
          $(e.target).addClass('transform')
        }
        if ($('.transform').length>2) {
          Array.from($('.transform')).forEach(v => {
            $(v).html('?')
          })
          $('.transform').removeClass('transform')
        }
        let arr = []
        Array.from($('.transform')).forEach(v => {
          arr.push($(v).attr('x')) 
        })
        if (arr.length === 2 && arr[0] === arr[1]) {
          $('.transform').addClass('binGo')
          $('.transform').removeClass('transform')
        }
        if ($('.binGo').length === $('.item').length) {
          $('.item').css('transform', 'translate(20px,-20px)')
          $('.item').html('win')
        }
      })
    }

添加动效

当我们点击元素时,希望能有一些好看的反馈,这里我借助css3 transition和transform属性,实现了元素漂移旋转特效

.item.transform{
    font-size: 10px !important;
    transform: rotateZ(360deg) rotateY(360deg);
    box-shadow: inset 0 0 10px 1px #1d4be9;
  }
  .binGo{
    font-size: 10px !important;
    transform: rotateZ(360deg) rotateY(360deg);
    box-shadow: inset 0 0 10px 1px #ec0909;
  }

增加趣味性

这里通过原生的select提供了修改level的方式,level越大,需要匹配的元素就越多。

 <select
    name=""
    id="sel"
    >
    <option value="1">普通级</option>
    <option value="4">史诗级</option>
</select>
$('#sel').on('change', function (e) {
  level = Number($(this).val())
  initLLKan()
})