[前端] 使用js编写实现拼图游戏

2018 0
Honkers 2022-10-21 15:47:55 | 显示全部楼层 |阅读模式
本文实例为大家分享了用js编写实现拼图游戏的具体代码,供大家参考,具体内容如下
目标

使用原生js编写一个拼图游戏,我这里写了两种拼图的方法。一种是拖拽拼图,一种是经典的九宫格拼图,可以自定义参数设置游戏难度
先看看截图效果
拖拽模式(拖拽图片切换图片)


点击模式(点击图片与空白区域切换位置)


不多说,直接上代码
css
  1. #canvasBox{
  2.   margin: 0 auto;
  3.   position: fixed;
  4.   border: 2px solid #f00;
  5.   overflow: hidden;
  6. }
  7. .item{
  8.   display: inline-block;
  9.   border: 1px solid #f00;
  10.   position: absolute;
  11.   top: 0;
  12.   left: 0;
  13.   transition: 0.1s;
  14. }
复制代码
html
  1. <div style="margin: 0 auto;text-algin:center;">
  2.   <button >拖拽模式</button>
  3.   <button  >点击模式</button>
  4. </div>
  5. <div id="canvasBox"></div>
  6. <div id="canvasBox2"></div>
复制代码
javascript
  1. /*
  2.     * @title JigsawGame 拼图游戏
  3.     * @params obj Object 游戏参数
  4.     * {
  5.     *  @param Id String 容器id
  6.     *  @param imgUrl String 图片路径
  7.     *  @param level Number 游难度 简单:1 || 普通:2 || 困难:3
  8.     *  @param gameType Number 游戏类型 拖动版本:1 || 点击版本:2
  9.     * }
  10.     * @author beideng
  11.     */
  12.    function JigsawGame(obj){
  13.     // 初始化容器
  14.     this.Id = document.getElementById(obj.Id);
  15.     // 初始化图片
  16.     this.img = new Image();
  17.     this.img.src = obj.imgUrl;
  18.     // 容器最大宽度
  19.     this.windowWidth = document.body.clientWidth;
  20.     this.maxWidth = this.windowWidth > 750 ? 750 : (this.windowWidth * 0.9);
  21.     // 设置容器宽高
  22.     this.Id.style.width = this.maxWidth + "px";
  23.     this.Id.style.height = this.maxWidth + "px";
  24.     this.Id.style.left = (this.windowWidth - this.maxWidth)/2 + "px";
  25.     this.Id.style.top = 50 + "px";
  26.     // 获取容器范围
  27.     this.boxOffsetY = parseFloat(this.Id.style.top);
  28.     this.boxOffsetX = parseFloat( this.Id.style.left);
  29.     // 关卡(简单:1 || 普通:2 || 困难:3)
  30.     if(obj.level == 1 || obj.level == 2 || obj.level == 3 ){
  31.      this.Level = obj.level;
  32.     }else{
  33.      this.level = 1;
  34.     }
  35.     // 难度
  36.     var diffArr = [3, 4, 5];
  37.     this.Diff= diffArr[this.Level-1];
  38.     // canvas宽高
  39.     this.cW = this.maxWidth/ this.Diff;
  40.     this.cH = this.maxWidth/ this.Diff;
  41.     // 记录的小方块个数
  42.     this.number = 0;
  43.     // 正确的数组
  44.     this.numberArr = [];
  45.     // 存储小方块的中心点坐标
  46.     this.centerXY = [];
  47.     /*
  48.      * 获取游戏类型
  49.      */
  50.     this.gameType = obj.gameType || 1;
  51.     // 记录最后一个元素的标记
  52.     this.lastElement = {
  53.      sign: 0,
  54.      left: 0,
  55.      top: 0,
  56.      Id: obj.Id + 1
  57.     };
  58.     // 初始化
  59.     this.Init();
  60.    }
  61.    /*
  62.     * 操作方法 *
  63.     */
  64.    JigsawGame.prototype = {
  65.     /*
  66.      * @method 初始化
  67.      */
  68.     Init: function(){
  69.      var that = this;
  70.      this.img.onload = function(){
  71.       // 格子宽高
  72.       var LevelW = that.img.width/that.Diff;
  73.       var LevelH = that.img.height/that.Diff;
  74.       for(var i = 0 ; i < that.Diff; i++){
  75.        for(var j = 0 ; j < that.Diff; j++){
  76.         // 初始化小方块
  77.         that.initCube(i, j, LevelW, LevelH);
  78.        }
  79.       }
  80.       // 打乱小方块
  81.       that.upsetElement();
  82.       // 游戏类型判断
  83.       if(that.gameType == 1){
  84.        // 监听拖动
  85.        that.Id.addEventListener("mousedown",function(event){
  86.         that.mouseDown(event);
  87.        }, false);
  88.       }else{
  89.        // 获取空白小方块坐标
  90.        that.getLastElement();
  91.        // 监听点击
  92.        that.Id.addEventListener("click",function(event){
  93.         that.mouseClick(event);
  94.        }, false);
  95.       }
  96.      }
  97.     },
  98.     /*
  99.      * @method 初始化小方块
  100.      * @param i Number 循环值
  101.      * @param j Number 循环值
  102.      * @param j LevelW 小方块宽
  103.      * @param j LevelH 小方块高
  104.      */
  105.     initCube: function(i, j, LevelW, LevelH){
  106.      // 创建一个小方块
  107.      var item = document.createElement("div"),
  108.       cW = this.cW,
  109.       cH = this.cH;
  110.      item.className = "item";
  111.      item.setAttribute("data-index", this.number);
  112.      item.style.width = cW + "px";
  113.      item.style.height = cH + "px";
  114.      item.style.left = i * cW + "px";
  115.      item.style.top = j * cH + "px";
  116.      item.innerHTML = "<canvas class='' width='"+ cW +"' height='"+ cH +"'></canvas>";
  117.      this.Id.appendChild(item);
  118.      var canvas = item.querySelector("canvas");
  119.      var ctx = canvas.getContext("2d");
  120.      /*
  121.       * 当游戏为点击类型时
  122.       * 去掉最后一个小方块里的图片
  123.       * 且记录当前元素的坐标以及编号
  124.       */
  125.      if(this.gameType != 1 && j == this.Diff-1 && i == this.Diff-1){
  126.       this.lastElement.sign = this.number;
  127.       item.id = this.lastElement.Id;
  128.      }else{
  129.       ctx.drawImage(this.img, i * LevelW, j * LevelH , LevelW, LevelH, 0 , 0, cW, cH)
  130.      }
  131.      // 每添加一个就压入一次到数组
  132.      this.numberArr.push({
  133.       x: i*cW +"px" ,
  134.       y: j*cH +"px"
  135.      });
  136.      this.number++;
  137.      // 压入初始中心点
  138.      this.centerXY.push({
  139.       x: i*cW + cW / 2,
  140.       y: j*cH + cH / 2
  141.      });
  142.     },
  143.     /*
  144.      * @method 悬停拖住小方块
  145.      * @param event Object 鼠标对象
  146.      */
  147.     mouseDown: function(event){
  148.      console.log(event)
  149.      var event = event || window.event;
  150.      var that = this;
  151.      var target = event.target || event.srcElement;
  152.      // 保证拖动的是想要的元素
  153.      if( target.parentElement.className.toLowerCase() == "item"){
  154.       var Element = target.parentElement;
  155.       // 存储当前元素的top,left
  156.       var thisTop = parseFloat( Element.style.top );
  157.       var thisLeft = parseFloat( Element.style.left );
  158.       // 获取当前点击的位置
  159.       var pageX = event.pageX;
  160.       var pageY = event.pageY;
  161.       // 拖动
  162.       document.onmousemove = function(e){
  163.        console.log(e)
  164.        that.mouseMove(e, Element, thisTop, thisLeft, pageY, pageX);
  165.        return false;
  166.       }
  167.       // 松开
  168.       document.onmouseup = function(e){
  169.        that.mouseUp(e, Element, thisTop, thisLeft)
  170.        // 释放拖拽
  171.        document.onmousemove = null;
  172.        document.onmouseup = null;
  173.        return false;
  174.       }
  175.      }
  176.      return false;
  177.     },
  178.     /*
  179.      * @method 拖动小方块
  180.      * @param e Object 鼠标对象
  181.      */
  182.     mouseMove: function(e, Element, thisTop, thisLeft, pageY, pageX){
  183.      var pageX2 = e.pageX;
  184.      var pageY2 = e.pageY;
  185.      Element.style.top = thisTop + (pageY2 - pageY) + "px";
  186.      Element.style.left = thisLeft + (pageX2 - pageX) + "px";
  187.      Element.style.zIndex = 1000;
  188.     },
  189.     /*
  190.      * @method 松开小方块
  191.      * @param e Object 鼠标对象
  192.      */
  193.     mouseUp: function(e, Element, thisTop, thisLeft){
  194.      var that = this,
  195.         cW = this.cW,
  196.        cH = this.cH;
  197.      // 检测当前拖动替换目标
  198.      var moveCenterX = parseFloat(Element.style.left) + cW / 2;
  199.      var moveCenterY = parseFloat(Element.style.top) + cH / 2;
  200.      var changeElementIndex = this.checkChangeElement(moveCenterX, moveCenterY);
  201.      var changeElement = this.Id.getElementsByClassName("item")[changeElementIndex];
  202.      // 限制拖拽范围
  203.      // 当松开的坐标xy在容器范围内
  204.      if( e.pageX < this.boxOffsetX || e.pageX > (this.boxOffsetX + this.maxWidth) || e.pageY < this.boxOffsetY || e.pageY > (this.boxOffsetY + this.maxWidth) ){
  205.       console.log("释放")
  206.       Element.style.top = thisTop + "px";
  207.       Element.style.left = thisLeft + "px";
  208.      }else{
  209.       // 判断当前元素是否离开了自己的格子
  210.       if( Element.getAttribute("data-index") == changeElement.getAttribute("data-index")){
  211.        Element.style.top = thisTop + "px";
  212.        Element.style.left = thisLeft + "px";
  213.       }else{
  214.        // 进行替换
  215.        Element.style.top = changeElement.style.top ;
  216.        Element.style.left = changeElement.style.left ;
  217.        changeElement.style.top = thisTop + "px";
  218.        changeElement.style.left = thisLeft + "px";
  219.        changeElement.style.zIndex = 1000;
  220.        // 更新小方块中心点
  221.        this.updateElement();
  222.       }
  223.      }
  224.      // 消除层级问题
  225.      setTimeout(function(){
  226.       Element.style.zIndex = 0;
  227.       changeElement.style.zIndex = 0;
  228.       if(that.compareArray()){
  229.        alert("恭喜你,拼图成功!");
  230.       }
  231.      }, 150);
  232.      // 判断拼图完成
  233.      console.log(this.compareArray())
  234.      console.log(this.numberArr)
  235.     },
  236.     /*
  237.      * @method 检测当前拖动替换目标
  238.      * @param moveLeft Number 鼠标移动的x值
  239.      * @param moveTop Number 鼠标移动的y值
  240.      * @return minIndex Number 返回目标对象下标
  241.      * 通过三角函数检测当前拖动对象中心点和其他所有对象中心点距离,离谁最近就和谁替换
  242.      */
  243.     checkChangeElement: function(moveLeft, moveTop){
  244.      // 最小距离
  245.      var minDistance = null;
  246.      // 最小距离替换目标
  247.      var minIndex = null;
  248.      for(var i = 0 ; i < this.centerXY.length; i++){
  249.       var x = Math.abs( moveLeft - this.centerXY[i].x );
  250.       var y= Math.abs( moveTop - this.centerXY[i].y );
  251.       var val = Math.ceil(Math.sqrt( x * x + y * y));
  252.       // 初次判断
  253.       if(minDistance == null){
  254.        minDistance = val;
  255.        minIndex = i;
  256.       }
  257.       // 后续判断
  258.       if(minDistance > val){
  259.        minDistance = val;
  260.        minIndex = i;
  261.       }
  262.      }
  263.      // 返回目标对象下标
  264.      return minIndex;
  265.     },
  266.     /*
  267.      * @method 更新小方块中心点
  268.      */
  269.     updateElement: function(){
  270.      var allElement = this.Id.getElementsByClassName("item"),
  271.        cW = this.cW,
  272.        cH = this.cH;
  273.      this.centerXY = [];
  274.      for(var i = 0 ; i < allElement.length; i++){
  275.       this.centerXY.push({
  276.        x: parseFloat(allElement[i].style.left) + cW / 2,
  277.        y: parseFloat(allElement[i].style.top) + cH / 2
  278.       });
  279.      }
  280.     },
  281.     /*
  282.      * @method 点击小方块
  283.      * @param event Object 鼠标对象
  284.      * @ 1、点击当前非空白小方块
  285.      * @ 2、获取其坐标,并加减一个一个方块宽度,用这个加减坐标去检索空白小方块是否在目标小方块周边
  286.      * @ 3、如果在,则替换这两个小方块的坐标
  287.      */
  288.     mouseClick: function(event){
  289.      console.log(event)
  290.      var event = event || window.event;
  291.      var that = this;
  292.      var target = event.target || event.srcElement;
  293.      // 保证拖动的是想要的元素
  294.      if( target.parentElement.className.toLowerCase() == "item"){
  295.       var Element = target.parentElement;
  296.       // 当当前点击目标为空白小方块时,终止函数
  297.       if(Element.getAttribute("data-index") == this.lastElement.sign){
  298.        return ;
  299.       }
  300.       // 存储当前元素的top,left
  301.       var thisTop = parseFloat( Element.style.top );
  302.       var thisLeft = parseFloat( Element.style.left );
  303.       // 点击检测空白方块是否在当前对象周边
  304.       if(this.mouseClickCheck(thisTop, thisLeft)){
  305.        console.log(222)
  306.        // 获取空白元素
  307.        var lastElement = document.getElementById(this.lastElement.Id);
  308.        // 替换这两个元素的坐标
  309.        Element.style.top = lastElement.style.top;
  310.        Element.style.left = lastElement.style.left;
  311.        lastElement.style.top = thisTop + "px";
  312.        lastElement.style.left = thisLeft + "px";
  313.        this.lastElement.left = thisLeft ;
  314.        this.lastElement.top = thisTop;
  315.        // 消除层级问题
  316.        setTimeout(function(){
  317.         if(that.compareArray()){
  318.          alert("恭喜你,拼图成功!");
  319.         }
  320.        }, 150);
  321.        // 判断拼图完成
  322.        console.log(this.compareArray())
  323.        console.log(this.numberArr)
  324.       }
  325.      }
  326.      return false;
  327.     },
  328.     /*
  329.      * @method 点击检测空白方块是否在当前对象周边
  330.      * @param thisTop Number 当前点击元素的top
  331.      * @param thisLeft NUmber 当前点击元素的left
  332.      * @return Boolean 是否在周边
  333.      */
  334.     mouseClickCheck: function(thisTop, thisLeft){
  335.      var cW = this.cW,
  336.        cH = this.cH;
  337.      if(thisTop == this.lastElement.top && (thisLeft - cH) == this.lastElement.left){
  338.       return true;
  339.      }
  340.      if(thisTop == this.lastElement.top && (thisLeft + cH) == this.lastElement.left){
  341.       return true;
  342.      }
  343.      if((thisTop - cW) == this.lastElement.top && thisLeft == this.lastElement.left){
  344.       return true;
  345.      }
  346.      if((thisTop + cW) == this.lastElement.top && thisLeft == thi.lastElement.left){
  347.       return true;
  348.      }
  349.      return false;
  350.     },
  351.     /*
  352.      * @method 获取空白元素left,right
  353.      */
  354.     getLastElement: function(){
  355.      // 获取空白元素
  356.      var lastElement = document.getElementById(this.lastElement.Id);
  357.      console.log(this.lastElement);
  358.      this.lastElement.left = parseFloat(lastElement.style.left) ;
  359.      this.lastElement.top = parseFloat(lastElement.style.top);
  360.     },
  361.     /*
  362.      * @method 打乱小方块
  363.      * 以小方块的个数为次数,每次随机抽取两个小于小方块的数,然后替换两个dom元素的定位坐标
  364.      */
  365.     upsetElement: function(){
  366.      for (var i = 0; i < this.number-1; i++) {
  367.       // 获取两个不相等的随机值
  368.       var n1 = Math.floor(Math.random()*this.number);
  369.       var n2 = Math.floor(Math.random()*this.number);
  370.       do{
  371.        n2 = Math.floor(Math.random()*this.number);
  372.       }while(n1 == n2)
  373.       // 替换当前的两个小方块的坐标
  374.       var allElement = this.Id.getElementsByClassName("item");
  375.       var Top = allElement[n1].style.top ;
  376.       var Left = allElement[n1].style.left ;
  377.       allElement[n1].style.top = allElement[n2].style.top ;
  378.       allElement[n1].style.left = allElement[n2].style.left ;
  379.       allElement[n2].style.top = Top ;
  380.       allElement[n2].style.left = Left ;
  381.      }
  382.     },
  383.     /*
  384.      * @method 比较小方块是否拼图完成
  385.      * @return boolean
  386.      * 获取切换小方块后,获取小方块的序号并与正确排序数组进行比较
  387.      */
  388.     compareArray: function(){
  389.      // 获取序号
  390.      var allElement = this.Id.getElementsByClassName("item");
  391.      for(var i = 0; i < this.number-1; i++){
  392.       // 比较序号
  393.       if( this.numberArr[i].x != allElement[i].style.left || this.numberArr[i].y != allElement[i].style.top ){
  394.        return false;
  395.       }
  396.      }
  397.      return true;
  398.     },
  399.    }
  400.    // 实例化一个对象
  401.    var box = new JigsawGame({
  402.      Id: 'canvasBox',
  403.      imgUrl: '../image/lingtai.jpg',
  404.      level: 1,
  405.      gameType: 1
  406.    });
  407.    // 实例化一个对象
  408.    var box2 = new JigsawGame({
  409.      Id: 'canvasBox2',
  410.      imgUrl: '../image/lingtai.jpg',
  411.      level: 1,
  412.      gameType: 2
  413.    });
  414.    function setGame(a, b){
  415.      document.getElementById("canvasBox").style.display = a;
  416.      document.getElementById("canvasBox2").style.display = b;
  417.    }
  418.    setGame("block", "none");
复制代码
稍微修改一下样式和触发事件,就是一个h5版本的demo。由于没用到项目里,没有考虑兼容问题
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持中国红客联盟。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4009
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行