含笑阁 发表于 2019-3-20 23:59

【web】纯JavaScript实现window经典纸牌游戏

本帖最后由 含笑阁 于 2019-3-21 10:53 编辑

写在最开头:JS无所不能!!!
昨天刚好看到html5新功能中的拖放事件,于是心血来潮,想用这个功能做一个纸牌游戏。
目前有个小?Bug,不能同时拖动多张扑克;{:301_1008:}
测试地址:http://www.lingmx.com/poker/

接下来给大家说一下思路,以及实现的代码
先把最先单的CSS和HTML代码片段发出来吧,只有CSS和HTML的话,效果显示如下:

CSS代码片段:
                        body {
                              margin: 0;
                              padding: 0;
                              background: url(img/bg.png);
                        }

                        #main {
                              width: 100%;
                              height: 100%;
                        }

                        .nav {
                              width: 100%;
                              height: 200px;
                              display: flex;
                              justify-content: space-around;
                              align-items: center;
                        }

                        #allPoker,
                        #fourPokers {
                              display: flex;
                              justify-content: space-around;
                              align-items: center;
                        }

                        #fourPokers div,
                        #sevenPokers div,
                        #allPoker div {
                              width: 105px;
                              height: 150px;
                              margin: 0 20px;
                              box-sizing: border-box;
                              border: 2px solid rgba(255, 255, 255, 0.6);
                              border-radius: 5px;
                              cursor: pointer;
                              position: relative;
                        }

                        #fourPokers div img,
                        #sevenPokers div img,
                        #allPoker div img {
                              position: relative;
                              border-radius: 5px;
                        }

                        #allPoker div:nth-child(1).active:after {
                              content: "";
                              width: 105px;
                              height: 150px;
                              position: absolute;
                              left: 0;
                              top: 0;
                              background: url(img/01.png);
                        }

                        #sevenPokers {
                              display: flex;
                              justify-content: space-around;
                              align-items: center;
                        }

                        #fourPokers div img,
                        #sevenPokers div img,
                        #allPoker div img {
                              position: absolute;
                              left: 0;
                              top: 0;
                        }

HTML代码片段:
                <div id="main">
                        <!-- 顶部牌堆 -->
                        <div class="nav">
                              <!-- 左侧牌堆 -->
                              <div id="allPoker">
                                        <div class="active"></div>
                                        <div></div>
                              </div>
                              <!-- 右侧4个牌堆 -->
                              <div id="fourPokers">
                                        <div></div>
                                        <div></div>
                                        <div></div>
                                        <div></div>
                              </div>
                        </div>
                        <!-- 底部 7个牌堆-->
                        <div id="sevenPokers">
                              <div></div>
                              <div></div>
                              <div></div>
                              <div></div>
                              <div></div>
                              <div></div>
                              <div></div>
                        </div>
                </div>

首先:说一下思路,网页整体分为三个关键位置
1.左上角主牌堆
2.右上角4个放置牌的区域
3.下方7个牌堆
我们需要床架一个随机生成没有重复的0~51的数保存到数组中【共52张扑克,没有大小王】,
然后把所有的图片对象保存到另一个数组中,通过第一个数组中保存的下标去取第二个数组中的图片,
这样就能保证每次取出的图片都是随机的


接下来就是按照游戏规则修改拖放事件和拖放释放事件

好了,接下来是正题:
直接放出完整的代码吧:我在代码里加了详细?的注释应该可以看的明白
// 循环遍历将poker图片添加到数组中
                        var pokersList = [];
                        var flower = 0;
                        for (var i = 1; i <= 52; i++) {
                              var poker = new Image();
                              poker.src = "img/" + i + ".jpg"
                              poker.setAttribute("data-i",i-flower*13); //保存扑克的值
                              poker.setAttribute("data-flower",flower); //保存扑克的花色
                              pokersList.push(poker);
                              if(i%13==0){flower++}
                        }

                        console.log(pokersList);

                        //随机生成长度为52的数组 保存下标,0~51;
                        var pokerObjI = [];
                        for (var i = 0; pokerObjI.length < 52; i++) {
                              var num = Math.floor(Math.random() * 52);
                              var isRepeat = false;
                              for (var item of pokerObjI) {
                                        if (item == num) {
                                                isRepeat = true;
                                        }
                              }
                              if (!isRepeat) {
                                        pokerObjI.push(num);
                              }
                        }

                        // -----------------向所有牌堆中添加扑克-----------------
                        // 创建全局变量 pokerNum 来保存以添加过的最后一个下标
                        var pokerNum = 0;
                        // 获取牌堆对象
                        var allPoker = document.getElementById('allPoker').children;
                        // 向牌堆中插入24张扑克
                        for (var i = 0; i < 24; i++) {
                              allPoker.appendChild(pokersList]);
                              pokerNum++; //同时样poker的下标增加
                        }
                        // 向下方7个牌堆中分别保存 1,2,3,4,5,6,7张扑克(共28张)
                        // 获取7个牌堆对象
                        var sevenObj = document.getElementById('sevenPokers').children;
                        // console.log(sevenObj)
                        // 循环向牌堆中添加扑克
                        for (var i = 1; i <= 7; i++) {
                              for (var j = 1; j <= i; j++) {
                                        sevenObj.appendChild(pokersList]);
                                        pokerNum++;
                              }
                        }

                        // ----------------为下方7个牌堆添加层叠样式-----------------
                        // 给除最后一张扑克外添加active样式
                        function addClass(){
                              for (var item of sevenObj) {
                                        var items = item.children;
                                        try{
                                                for(var j of items){
                                                      var src = j.getAttribute("data-src");
                                                      if(src !==null){j.src = src;j.removeAttribute("data-src")};
                                                }
                                        }catch(e){
                                                //TODO handle the exception
                                        }
                                        if (items.length > 1) {
                                                for (var i = items.length - 2; i >= 0; i--) {
                                                      items.setAttribute("data-src", items.src);
                                                      items.src = "img/01.png";
                                                }
                                        }
                              }      
                        }
                        
                        // 判断7个牌堆中如果有最后一个扑克均为背面,则让最后一个变为正面,
                        // 如果最后一个为正面,则什么都不改变
                        function changeClass(){
                              for (var item of sevenObj){
                                        var lastItem = item.children;
                                        var penultimate = item.children;
                                        if(item.children.length>0){
                                                if(penultimate==undefined){
                                                      if(lastItem.dataset.src!=null){
                                                                console.log(lastItem);
                                                                console.log(lastItem.src);
                                                                console.log(lastItem.dataset.src);
                                                                lastItem.src = lastItem.dataset.src;
                                                      }
                                                }else if(penultimate.src == lastItem.src){
                                                      // 如果倒数第二个扑克和倒数第一个扑克src相等,让最后一个变为正面
                                                      lastItem.src = lastItem.dataset.src;
                                                }
                                        }
                              }
                        }
                        
                        // 给扑克添加上外边距 让他们分开
                        function addMarginTop() {
                              for (var item of sevenObj) {
                                        var items = item.children;
                                        for(var i of items){
                                                i.removeAttribute("style");
                                        }
                                        if (items.length > 1) {
                                                for (var i = 1; i < items.length; i++) {
                                                      items.style.marginTop = i * 30 + "px";
                                                }
                                        }
                              }
                        }
                        
                        // 调用上方的函数
                        addClass();
                        addMarginTop();

                        // ----------在所有扑克分发结束后绑定事件----------

                        //为牌堆中poker绑定点击事件 [点击显示一张,全部显示完后再次点击让所有牌回到牌堆]
                        // 1.获取到所有牌堆中的扑克
                        var AllpokerObj = allPoker.getElementsByTagName('img');
                        // 2.获取牌堆展示的div
                        var AllpokerDiv = allPoker.nextElementSibling;
                        // 创建变量保存被拖动的扑克和展示区域当前已添加的扑克数
                        var thisPoker, pakerListNum;
                        // 创建变量保存当前被拖动扑克的花色和值
                        var thisI,thisFlower;
                        // 为牌堆添加点击事件
                        allPoker.onclick = function() {
                              // 将展示区域的所有扑克保存在list中
                              var pokerList = AllpokerDiv.getElementsByTagName('img');
                              // 创建变量保存展示区域扑克个数
                              pakerListNum = pokerList.length;
                              // console.log(pokerList);
                              if (AllpokerObj.length > 0) {
                                        AllpokerDiv.appendChild(AllpokerObj); //将最后一张扑克(最上方的),移动到展示区域
                                        if (AllpokerObj.length == 0) {
                                                allPoker.classList.remove('active')
                                        } //如果牌堆没有扑克了,去掉active样式(取消扑克背面样式)
                              } else {
                                        allPoker.classList.add('active') //为牌堆添加active样式
                                        for (var i = pakerListNum - 1; i >= 0; i--) {
                                                allPoker.appendChild(pokerList); //循环遍历将展示区域扑克添加到牌堆中
                                        }
                              }
                              // console.log(pakerListNum);

                              // 为牌堆展示框中所有扑克绑定拖动事件写在点击事件里,每次点击都更新
                              // 创建变量保存鼠标位置
                              var left, top;
                              for (item of pokerList) {

                                        item.ondragstart = function(e) {
                                                console.log("拖动开始")
                                                thisPoker = this;
                                                thisI = this.dataset.i;
                                                thisFlower = this.dataset.flower;
                                                //保存当前鼠标在扑克上的位置
                                                left = e.clientX - this.offsetLeft;
                                                top = e.clientY - this.offsetTop;
                                        }
                                        item.ondrag = function(e) {
                                                // console.log("拖动ing~")
                                                // 设置当前扑克的位置
                                                //this.style.position = "fixed"
                                                this.style.top = e.clientY - top + "px";
                                                this.style.left = e.clientX - left + "px";
                                        }
                                        item.ondragend = function() {
                                                console.log("拖动结束")
                                                // 移除之前添加的样式
                                                this.removeAttribute("style")
                                        }
                              }
                        }
                        // 为7个牌堆的最后一个扑克添加拖动事件
                        function addDrag(){
                              for (var item of sevenObj) {
                                        var list = item.children;
                                        var left,op,style;
                                        if(list){
                                                list.ondragstart = function(e) {
                                                      console.log("拖动开始")
                                                      thisPoker = this;
                                                      thisI = this.dataset.i;
                                                      thisFlower = this.dataset.flower;
                                                      //保存当前鼠标在扑克上的位置
                                                }
                                                list.ondrag = function(e) {
                                                }
                                                list.ondragend = function() {
                                                      console.log("拖动结束")
                                                      // 移除之前添加的样式
                                                      //this.removeAttribute("style");
                                                      //this.setAttribute("style",style);
                                                }
                                        }
                                       
                              }               
                        }
                        addDrag();
                        // 为4个牌堆添加拖动释放事件
                        // 获取4个牌堆
                        var fourPokers = document.getElementById('fourPokers').children;
                        for (var item of fourPokers) {
                              item.ondragenter = function() {
                                        console.log("拖动进入")
                              }
                              item.ondragover = function(e) {
                                        e.preventDefault();
                                        console.log("拖动悬停")
                              }
                              item.ondragleave = function() {
                                        console.log("拖动离开")
                              }
                              item.ondrop = function() {
                                        console.log(this.children.length)
                                        // 当本牌堆里没有扑克时,只可放入A,即thisI为1的扑克
                                        if(this.children.length==0){
                                                // 判断当前拖动的扑克的值不等于1则停止运行 return
                                                if(thisI != 1){
                                                      console.log("只可放入A");
                                                      return;
                                                }
                                        }else{ // 当牌堆中有扑克时,先判断当前拖动扑克的值和花色,如果与牌堆中的花色不同and值不大于最后一个扑克1时,停止运行
                                                // 获取当前牌堆的data-i和data-flower
                                                var itemI = parseInt(this.dataset.i);
                                                var itemFlower = this.dataset.flower;
                                                if(thisFlower != itemFlower || (thisI - itemI) != 1){
                                                      console.log("仅可放入同花色且值仅能比当前值大1")
                                                      return;
                                                }
                                        }
                                       
                                        console.log("拖动释放")
                                        console.log(thisPoker);
                                        console.log(thisI);
                                        console.log(thisFlower);
                                        console.log(this);
                                        this.appendChild(thisPoker);
                                        this.setAttribute("data-i",thisI);
                                        this.setAttribute("data-flower",thisFlower);
                                        for(var i of this.children){
                                                i.removeAttribute("style");
                                        }
                                        addDrag();
                                        changeClass();
                                        // pakerListNum--;
                              }
                        }
                        
                        // 为下方7个牌堆添加拖动释放事件
                        for(var item of sevenObj){
                              item.ondragenter = function() {
                                        console.log("拖动进入")
                              }
                              item.ondragover = function(e) {
                                        e.preventDefault();
                                        console.log("拖动悬停")
                              }
                              item.ondragleave = function() {
                                        console.log("拖动离开")
                              }
                              item.ondrop = function() {
                                        console.log(this.children.length)
                                        if(this.children.length==0){
                                                // 当本牌堆里没有扑克时,只可放入K,即thisI为13的扑克
                                                // 判断当前拖动的扑克的值不等于1则停止运行 return
                                                if(thisI != 13){
                                                      console.log("只可放入K");
                                                      return;
                                                }
                                        }else{
                                                var thisItem = this.children;
                                                var itemI = parseInt(thisItem.dataset.i);
                                                var itemFlower = parseInt(thisItem.dataset.flower);
                                                if(thisFlower%2 == itemFlower%2 || itemI - thisI !=1){
                                                      console.log("只能放置不同的花色,并且值执行相差1")
                                                      return;
                                                }
                                        }
                                        console.log("拖动释放");
                                        this.appendChild(thisPoker)
                                        for(var i of this.children){
                                                i.removeAttribute("style");
                                        }
                                        addDrag();
                                        addMarginTop();
                                        changeClass();
                                        // pakerListNum--;
                              }
                        }

打包下载:https://www.lanzouj.com/i3hz4ih

随风浅忆 发表于 2019-5-22 20:17

本帖最后由 随风浅忆 于 2019-5-22 20:19 编辑

我在楼主的基础上进行了部分修改

[*]结构分离:将css和js从HTML里分离为单个文件
[*]去除了左上角牌堆的背景图
[*]牌组数组的img预设为背面,在显示时将其翻面
[*]删除addClass()函数,在向下方七个牌堆添加牌时判断是否为最下一张,并将其翻为正面
[*]修改了牌堆点击事件内的牌堆翻面和恢复
[*]添加新变量thisList(Array),保存在下方牌堆拖动牌时的拖拽牌下方的牌
[*]在下方牌堆ondrop事件下判断thisList是否为空,若不为空则将其中的牌同时移动到目标牌堆

yzy9952 发表于 2019-3-21 00:05

沙发支持!最爱Windows纸牌。

kamui 发表于 2019-3-21 00:12

有意思,js能干的是多,不过node.js性能不好啊……

caipubao 发表于 2019-3-21 00:12

感谢楼主分享

wzq0391 发表于 2019-3-21 00:18

xiexiegnexiang

dongtian123sss 发表于 2019-3-21 00:20

支持一下

zero0226 发表于 2019-3-21 00:54

yangaiday 发表于 2019-3-21 01:36

大佬,厉害&#128077;

喂黑涩会的小姐 发表于 2019-3-21 07:27

没看明白,学习中,不太玩游戏

IceWind 发表于 2019-3-21 07:39

厉害了,竟然能用HTML5作出这游戏,膜拜……
页: [1] 2
查看完整版本: 【web】纯JavaScript实现window经典纸牌游戏