基于jquery实现十三点卡牌游戏

1,033 阅读3分钟

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

前言

最近迷上了拿Js复刻各类经典小游戏,今天就是尝试复刻一下13点游戏,要实现的功能就是两张牌加起来等于13点就消除,然后还要加上一些消牌动效,切牌动效,初始化动效之类的

卡牌桌面

桌面的话,很简单就是一个700*800的长方形 , moveL和moveR是换牌用的。一个是从左往右换 一个是从右往左换

<div class="table">
    <div class="moveL"> < </div>
    <div class="moveR"> > </div>
</div>
//css
  .table{
        width: 700px;
        height: 800px;
        margin:20px auto;
        position: relative;
        background-image: url("https://gimg2.baidu.com/image_search/src=http%3A%2F%2F2e.zol-img.com.cn%2Fproduct%2F102%2F840%2FceX1m2pmkRSs.jpg&refer=http%3A%2F%2F2e.zol-img.com.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1663638867&t=5dde1bdd205db8ec75ab2a52446dcdbf");
        background-position: center center;
        background-size: cover;
        background-repeat: no-repeat;
        color: #ffffff;
    }

生成卡牌

接下来就是在页面上动态创建卡牌,并随机赋予对应的卡牌,首先,一副牌除了大小王是52张,黑桃(Spade)、红桃(Heart)、方块(Diamond)、梅花(Club)每种13张,所以我们需要一副牌的img,并按数字+花色首字母进行命名

image.png 接下来就是通过代码来把牌放在页面上

image.png

首先先把牌进行初始化,花色4 数字13 不能重复

// 52 4  13
// hcsd 黑桃(Spade)、红桃(Heart)、方块(Diamond)、梅花(Club)
let poke=[];
let color=['h','s','c','d'];
let exist={};
// 这里就是循环产生52张牌的花色及下标,而且为了保证不重复,通过exist来保存已经生成的牌
for(let i=0;i<52;i++){
    let huase=color[Math.floor(Math.random()*4)];
    let shuzi=Math.floor(Math.random()*13+1);
    while (exist[`${huase}_${shuzi}`]){
        huase=color[Math.floor(Math.random()*4)];
        shuzi=Math.floor(Math.random()*13+1);
    }
    exist[`${huase}_${shuzi}`]=true;
    poke.push({huase,shuzi});
}

然后把牌以金字塔的形式放在页面上,这里因为上面卡牌生成是随机的,所以poke里52张牌的顺序也是随机的,相当于是进行了乱序洗牌的操作,想形成金字塔,第一时间我想到的就是双层for循环,第一行一张,第二行两张,依次类推,那就是相当于把第一层for循环的循环游标作为第二层for循环的结束条件

for(let i=0;i<7;i++){
        for(let j=0;j<=i;j++){
            let item=poke[index];
            //item={huase:shuzi}
            // $("<img>").appendTo(document.body).attr({src:`img/${item.shuzi}${item.huase}.png`});
            let imgSrc=`//pic.qy566.com/${item.shuzi}${item.huase}.png`;
            // $("img").remove();
          $('<div>').addClass('poke').css({
                backgroundImage:`url(${imgSrc})`
            }).delay(30*index)
              .prop({id:`${i}_${j}`}).attr({pokeid:`${item.shuzi}`})
              .data("num",item.shuzi)
                .animate({
                    left:300-i*50+100*j,
                    top:70*i,
                    opacity:1
                })
                .appendTo('.table');
        }
    }

形成金子塔还不算完,我们还需要把剩下的牌放在底部,供消除使用,这里上面两层for循环相当于等差数列求和,当然我们跑一次就会知道结果了,为了方便直接使用,我们声明一个下标,放在第二层for循环内让他自增,这样,循环完成后的下标就是poke未使用的第一个下标

let index= 0
...
 for(let j=0;j<=i;j++){
 ...
            index++;
            ...

把剩下的牌放进页面内,通过定位的方式放置在页面下方

 for(;index<poke.length;index++){
        let item=poke[index];
        let imgSrc=`//pic.qy566.com/${item.shuzi}${item.huase}.png`;
        $('<div>').addClass('poke zuo').attr({pokeid:`${item.shuzi}`})
            .data("num",item.shuzi)
            .css({
            backgroundImage:`url(${imgSrc})`
        }).delay(30*index)
            .animate({
                left:100,
                top:600,
                opacity:1
            })
            .appendTo('.table');
    }

然后。就是给牌添加点击事件,然后判断选中的两张牌和是否为13,他下面还有没有牌压着他,这里你们也注意到了,我给每张牌都添加了自定义属性叫做pokeid 值是对应的数字,这里任意花色都可组合,所以我们不考虑花色的问题,在初始化上面的卡牌的时候,我们把id定义为了i_j,这样,我们通过i+1 j+1存不存在,就知道下面有没有牌压着他了

 $('.poke').click(function () {
     // 拿到当前的id,并拿到他下面的左面那张和右面那张的id
    let coords = $(this).prop('id').split('_')
    let ele = $(`#${parseInt(coords[0]) + 1}_${parseInt(coords[1])}`)
    let ele1 = $(`#${parseInt(coords[0]) + 1}_${parseInt(coords[1]) + 1}`)
    // 判断假如被压着,那么就return  不做任何操作,没有被压着的时候,需要给一个动画效果,浮起
    if (!(ele.length || ele1.length)) {
      $(this).toggleClass('active')
      if ($(this).hasClass('active')) {
        $(this).finish().animate({ top: '-=20' })
      } else {
        $(this).finish().animate({ top: '+=20' })
      }
    } else {
      return
    }
    // 判断点的牌超过两张但是仍然没有达成条件,那就都移除浮起的效果
    if ($('.active').length > 2) {
      $('.active').animate({ top: '+=20' })
      $('.active').removeClass('active')
      // $(".active").eq(1).removeClass("active");
    }
    // 判断是否和为13 是的话,给一个飞出动画,并且移除对应dom元素
    $('.active').map(function (value, index) {
      for (let i = 0; i < $('.active').length; i++) {
        // console.log($(index).attr("pokeid"));
        if (parseInt($(index).attr('pokeid')) + parseInt($('.active').eq(i).attr('pokeid')) == 13 || $(index).attr('pokeid') == 13) {
          $('.active').animate({ top: 0, left: 600 }, function () {
            $(this).remove()
          })
        }
      }
    })
  })

接下来就是下面的两个左右箭头,可以操纵底下的备选卡牌进行左右移动

这里也是通过添加css样式+animation的方式实现的,所以就不粘贴代码了,具体代码都在代码片段内了

关键技术点

$.finish 这是为了保证能够流畅的应用动画,避免多次点击同一元素,不断执行添加移除样式导致的无限动画。

停止当前正在运行的动画,删除所有排队的动画,并完成匹配元素所有的动画。

当.finish()在一个元素上被调用,立即停止当前正在运行的动画和所有排队的动画(如果有的话),并且他们的CSS属性设置为它们的目标值(所有动画的目标值)。所有排队的动画将被删除。

如果第一个参数提供,该字符串表示的队列中的动画将被停止。

.finish()方法和.stop(true, true)很相似,.stop(true, true)将清除队列,并且目前的动画跳转到其最终值。但是,不同的是,.finish() 会导致所有排队的动画的CSS属性跳转到他们的最终值。