UDN-企业互联网技术人气社区

板块导航

浏览  : 412
回复  : 4

[原生js] JavaScript拼图游戏

[复制链接]
htmlman的头像 楼主
发表于 2017-1-10 15:16:04 | 显示全部楼层 |阅读模式
  无聊之余,看到了之前写的一个拼图游戏。就发一发共享下。写了有1年了。有些地方写得不是很好。但也能用了。

  先上图看效果:
1.png

  完整的代码可以去 git下载:https://github.com/zhouxitian/puzzle.

  这里主要说一下思路什么的。

  1、写这个之前也去搜索过网上的一些类似的游戏。当感觉都没有自己想要的。主要是网上的插件有些兼容不太好,例如移动端没兼容等。其次就是游戏难度控制感觉不好,有时过于困难有时过于简单。

  2、后来想想,还是自己动手写一个吧。这样比较符合自己想要的效果。

  3、主要考虑的事情其实就是一个。怎么控制游戏难度。不会出现太容易活太难的情况,也不会出现完成不了的情况。

  4、后来就想到了这个控制步数,来达到控制难度的方法。其实也很简单。就是难度为1。那么只要走1步就能完成游戏,难度为10。那么最少需要10步(难度控制在游戏总格数的一半比较好)。

  5、知道这类游戏规律的人会觉得容易,但不会的人却总在兜圈。玩多了,自然会感觉容易些。

  6、怎么控制步数来达到控制游戏难度这个效果呢。当时想的就是一个逆向思维。即 既然最后是要完成游戏,那么怎么 不一开始就当游戏是拼好的。然后根据难度随机走,尽量排除之前走过的,这样就能把游戏打乱。然后出来的结果能稳定的控制步数。

  7、总结:有时想问题总钻牛角尖,还不如静下来。逆向思考一下问题,或许就会获得灵感。

  为了方便,部分代码也发一发吧。完整的例子还是去git下:
  1. ;(function(window,undefined){
  2.     myPuzzle=function(opt){
  3.         var t=this;
  4.         t.options={
  5.             id:"game",
  6.             pic:"images/p1.jpg",//图片
  7.             x:4,//列
  8.             y:3,//行
  9.             hard:5,//最大难度最好不要大于总格数的一半
  10.             duration:100,//毫秒
  11.             startInit:null,//每次重新开始游戏时的回调(相当于初始化)
  12.             stepStart:null,//每步开始移动时的回调
  13.             finish:null,//游戏完成后的回调
  14.             stepEnd:null//每走一步后的回调
  15.         };
  16.         t.extend(t.options,opt);
  17.         t.container=t.getId(t.options.id);
  18.         t.length=t.options.x*t.options.y;
  19.         t.current={};//当前拖动的元素信息
  20.         t.move=true;//当前的移动对象是否li
  21.         t.isMove=false;//是否能移动
  22.         t.init();
  23.     }
  24.     myPuzzle.prototype={
  25.         init:function(){
  26.             var t=this;
  27.             t.createGrid();//排序
  28.             t.touch=new myTouch({//滑屏
  29.                 wrapper:"#"+t.wid,
  30.                 start:function(e){
  31.                     e=e||window.event;
  32.                     t.touchChild=e.target||e.srcElement;
  33.                     t.move=true;
  34.                     if(t.touchChild.nodeName.toLowerCase()!="li"){
  35.                         t.move=false;
  36.                     }
  37.                     t.checkMove=false;//是否需要检查 是否能移动
  38.                     t.isMove=false;//是否能移动
  39.                     if(t.move){
  40.                         t.touchChild.style.transitionDuration="0ms";
  41.                         t.current.index=parseInt(t.touchChild.getAttribute("index"));
  42.                         t.current.x=t.position[t.current.index].x*t.cWidth;
  43.                         t.current.y=t.position[t.current.index].y*t.cHeight;
  44.                         t.current._x=t.position[t.current.index]._x*t.cWidth;
  45.                         t.current._y=t.position[t.current.index]._y*t.cHeight;
  46.                     }
  47.                 }
  48.             });
  49.             t.start();
  50.             t.touch.moveX=t.touch.moveY=function(){
  51.                 if(t.move){
  52.                     var _t=this,MoveXY=0;
  53.                     if(!t.checkMove){//判断移动方向
  54.                         if(_t.x){
  55.                             if(_t.changeX<0){
  56.                                 t.direction="left";
  57.                             }else if(_t.changeX>0){
  58.                                 t.direction="right";
  59.                             }
  60.                             MoveXY=_t.changeX;
  61.                         }else if(_t.y){
  62.                             if(_t.changeY<0){
  63.                                 t.direction="up";
  64.                             }else if(_t.changeY>0){
  65.                                 t.direction="down";
  66.                             }
  67.                             MoveXY=_t.changeY;
  68.                         }
  69.                         t.isMove=t.canMove(t.position[t.current.index]._index,t.direction);
  70.                         if(t.isMove){//能移动才触发 每步的开始回调
  71.                             t.options.stepStart&&t.options.stepStart.call(t);
  72.                         }
  73.                         //console.log(t.position[t.index]);
  74.                         //console.log(t.getRadomPosition(t.position[t.index]._index))
  75.                         t.checkMove=true;
  76.                     }else{//限制移动范围
  77.                         if(_t.x){
  78.                             if(t.direction=="left"){
  79.                                 MoveXY=_t.changeX<-t.cWidth?-t.cWidth:(_t.changeX>0?0:_t.changeX);
  80.                             }else{
  81.                                 MoveXY=_t.changeX>t.cWidth?t.cWidth:(_t.changeX<0?0:_t.changeX);
  82.                             }
  83.                         }else if(_t.y){
  84.                             if(t.direction=="up"){
  85.                                 MoveXY=_t.changeY<-t.cHeight?-t.cHeight:(_t.changeY>0?0:_t.changeY);
  86.                             }else{
  87.                                 MoveXY=_t.changeY>t.cHeight?t.cHeight:(_t.changeY<0?0:_t.changeY);
  88.                             }
  89.                         }
  90.                     }
  91.                     if(t.isMove){
  92.                         if(_t.x){
  93.                             t.touchChild.style.cssText='transition-duration:0ms;-webkit-transition-duration:0ms;z-index:2;left:'+(t.current._x+MoveXY)+'px;top:'+t.current._y+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
  94.                         }else if(_t.y){
  95.                             t.touchChild.style.cssText='transition-duration:0ms;-webkit-transition-duration:0ms;z-index:2;left:'+t.current._x+'px;top:'+(t.current._y+MoveXY)+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
  96.                         }
  97.                     }   
  98.                 }
  99.             }
  100.             t.touch.endX=t.touch.endY=function(){
  101.                 if(t.move&&t.isMove){
  102.                     var _t=this,xy,_x=t.position[t.current.index]._x,_y=t.position[t.current.index]._y,_index=t.position[t.current.index]._index;
  103.                     if(_t.x){
  104.                         xy=t.position[t.index]._x*t.cWidth;
  105.                         t.touchChild.style.cssText='transition-duration:'+t.options.duration+'ms;-webkit-transition-duration:'+t.options.duration+'ms;z-index:0;left:'+xy+'px;top:'+t.current._y+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
  106.                     }else if(_t.y){   
  107.                         xy=t.position[t.index]._y*t.cHeight;
  108.                         t.touchChild.style.cssText='transition-duration:'+t.options.duration+'ms;-webkit-transition-duration:'+t.options.duration+'ms;z-index:0;left:'+t.current._x+'px;top:'+xy+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
  109.                     }
  110.                     //交换坐标
  111.                     t.position[t.current.index]._x=t.position[t.index]._x;
  112.                     t.position[t.current.index]._y=t.position[t.index]._y;
  113.                     t.position[t.current.index]._index=t.position[t.index]._index;
  114.                     t.position[t.index]._x=_x;
  115.                     t.position[t.index]._y=_y;
  116.                     t.position[t.index]._index=_index;
  117.                     var win=true;
  118.                     for(var i=t.position.length;i--;){
  119.                         if(t.position[i].index!=t.position[i]._index){
  120.                             win=false;
  121.                             break;
  122.                         }
  123.                     }
  124.                     t.touch.moveing=true;
  125.                     t.step++;
  126.                     if(win){
  127.                         setTimeout(function(){
  128.                             t.removeClass(t.visible,"hidden");
  129.                             t.options.finish&&t.options.finish.call(t);
  130.                         },t.options.duration);
  131.                     }else{
  132.                         setTimeout(function(){
  133.                             t.touch.moveing=false;
  134.                             t.options.stepEnd&&t.options.stepEnd.call(t);
  135.                         },t.options.duration);   
  136.                     }
  137.                 }
  138.             }
  139.         },
  140.         start:function(){
  141.             var t=this;
  142.             t.position=new Array();//记录每个移动元素的坐标
  143.             t.positionM=new Array();//记录移动后的元素
  144.             t.step=0;//记录移动的步数
  145.             if(t.visible){
  146.                 t.removeClass(t.visible,"hidden");
  147.             }
  148.             for(var i=0;i<t.length;i++){
  149.                 var x=i%t.options.x;
  150.                 var y=Math.floor(i/t.options.x);
  151.                 t.position.push({index:i,x:x,y:y,_index:i,_x:x,_y:y});
  152.                 t.positionM.push(i);
  153.                 x=x*t.cWidth;
  154.                 y=y*t.cHeight;
  155.                 t.wrapper.children[i].style.cssText='left:'+x+'px;top:'+y+'px;transition-duration:0ms;-webkit-transition-duration:0ms;background-position: -'+x+'px -'+y+'px;';
  156.             }
  157.             t.setGrid();//抽空一格
  158.             t.upSet();//打乱
  159.             t.touch.moveing=false;
  160.             t.options.startInit&&t.options.startInit.call(t);
  161.         },
  162.         refresh:function(){
  163.             var t=this;
  164.             t.width=parseInt(t.getStyles(t.container,"width"));
  165.             t.height=parseInt(t.getStyles(t.container,"height"));
  166.             t.cWidth=t.width/t.options.x;
  167.             t.cHeight=t.height/t.options.y;
  168.             for(var i=t.length;i--;){
  169.                 x=t.position[i].x*t.cWidth;//原始x坐标
  170.                 y=t.position[i].y*t.cHeight;//原始y坐标
  171.                 _x=t.position[i]._x*t.cWidth;//移动x坐标
  172.                 _y=t.position[i]._y*t.cHeight;//移动y坐标
  173.                 t.wrapper.children[i].style.cssText='left:'+_x+'px;top:'+_y+'px;background-position:-'+x+'px -'+y+'px;';
  174.             }
  175.         },
  176.         createGrid:function(){//排序
  177.             var t=this;
  178.             t.width=parseInt(t.getStyles(t.container,"width"));
  179.             t.height=parseInt(t.getStyles(t.container,"height"));
  180.             t.cWidth=t.width/t.options.x;
  181.             t.cHeight=t.height/t.options.y;
  182.             var position=t.getStyles(t.container,"position");
  183.             if(position=="static"){
  184.                 t.container.style.position="relative";
  185.             }
  186.             var ul=document.createElement('ul');
  187.             var data=new Date().getTime();
  188.             t.wid="myPuzzle_"+data;
  189.             ul.setAttribute("id",t.wid);
  190.             var style={};
  191.             style['#'+t.wid]='position:absolute;width:100%;height:100%;left:0;top:0;user-select:none;-webkit-user-select:none;';
  192.             style['#'+t.wid+' li']='transition-property:left,top,opacity;transition-timing-function:linear;-webkit-transition-property:left,top,opacity;-webkit-transition-timing-function:linear;width:'+(100/t.options.x)+'%;height:'+(100/t.options.y)+'%;position:absolute;background-image:url('+t.options.pic+');background-repeat:no-repeat;background-size:'+t.options.x+'00% '+t.options.y+'00%;';
  193.             style['#'+t.wid+' li.hidden']='visibility:hidden;opacity:0;';
  194.             //style['#'+t.wid+' li.hidden']='opacity:0.5;';
  195.             t.setCss(t.container,style);
  196.             var html='';
  197.             for(var i=0;i<t.length;i++){
  198.                 var x=i%t.options.x;
  199.                 var y=Math.floor(i/t.options.x);
  200.                 //t.position.push({index:i,x:x,y:y,_index:i,_x:x,_y:y});
  201.                 //t.positionM.push(i);
  202.                 x=x*t.cWidth;
  203.                 y=y*t.cHeight;
  204.                 html+='<li index='+i+'>'+i+'</li>';
  205.             }
  206.             ul.innerHTML=html;
  207.             t.container.appendChild(ul);
  208.             t.wrapper=t.getId(t.wid);
  209.         },
  210.         setGrid:function(){//随机空一格
  211.             var t=this;
  212.             var random=Math.round(Math.random()*(t.length-1));
  213.             t.visible=t.wrapper.children[random];
  214.             t.addClass(t.visible,"hidden");
  215.             t.index=random;
  216.         },
  217.         upSet:function(){//根据难度打乱排序
  218.             var t=this;
  219.             t.answer=new Array();//记录走法
  220.                 var answer=new Array();//记录移动方向
  221.                 for(var i=t.options.hard;i--;){
  222.                     var moveObj=t.getRadomPosition(t.position[t.index]._index);//记录能移动的元素(下标,方向);
  223.                     var random=Math.ceil(Math.random()*moveObj.length)-1;//在能移动的元素里随机一个移动
  224.                     ;(function(){//检查随机走向,修正使其不走重复的路径
  225.                         var length=answer.length;
  226.                         if(length>0){
  227.                             length--;
  228.                             //console.log("random:"+random);
  229.                             if((moveObj[random].direction=="up"&&answer[length]=="down")||(moveObj[random].direction=="down"&&answer[length]=="up")||(moveObj[random].direction=="left"&&answer[length]=="right")||(moveObj[random].direction=="right"&&answer[length]=="left")){
  230.                                 //console.log("相邻方向相反:"+moveObj[random].direction,answer[length],random);
  231.                                 if(random<moveObj.length-1){
  232.                                     random++;
  233.                                 }else{
  234.                                     random=0;
  235.                                 }
  236.                                 //console.log("相邻方向相反:"+moveObj[random].direction,answer[length],random);
  237.                                 arguments.callee();
  238.                             }else if(moveObj.length>2&&length>1&&((moveObj[random].direction=="up"&&answer[length-1]=="down")||(moveObj[random].direction=="down"&&answer[length-1]=="up")||(moveObj[random].direction=="left"&&answer[length-1]=="right")||(moveObj[random].direction=="right"&&answer[length-1]=="left"))){
  239.                                 //console.log(moveObj,answer)
  240.                                 for(var i=moveObj.length;i--;){
  241.                                     if(i!=random//不取当前的
  242.                                     &&!((moveObj[i].direction=="up"&&answer[length-1]=="down")||(moveObj[i].direction=="down"&&answer[length-1]=="up")||(moveObj[i].direction=="left"&&answer[length-1]=="right")||(moveObj[i].direction=="right"&&answer[length-1]=="left"))//随机方向对比上一个方向
  243.                                     &&!((moveObj[i].direction=="up"&&answer[length]=="down")||(moveObj[i].direction=="down"&&answer[length]=="up")||(moveObj[i].direction=="left"&&answer[length]=="right")||(moveObj[i].direction=="right"&&answer[length]=="left"))){//随机方向对比前一个方向
  244.                                         random=i;
  245.                                         //console.log("相隔方向相反:"+moveObj[random].direction,random);
  246.                                         break;
  247.                                     }
  248.                                 }
  249.                             }
  250.                         }
  251.                     })();   
  252.                     t.touchChild=t.container.querySelectorAll("li")[moveObj[random].index];
  253.                     //console.log(t.position[t.index].index+"换"+moveObj[random].index);
  254.                     t.answer.push(moveObj[random].index);
  255.                     answer.push(moveObj[random].direction);
  256.                     t.current.x=t.position[moveObj[random].index].x*t.cWidth;
  257.                     t.current.y=t.position[moveObj[random].index].y*t.cHeight;
  258.                     t.current._x=t.position[moveObj[random].index]._x*t.cWidth;
  259.                     t.current._y=t.position[moveObj[random].index]._y*t.cHeight;
  260.                     
  261.                     var xy,
  262.                     _x=t.position[moveObj[random].index]._x,//临时保存需交换的坐标
  263.                     _y=t.position[moveObj[random].index]._y,
  264.                     _index=t.position[moveObj[random].index]._index;
  265.                     
  266.                     if(moveObj[random].direction=="right"||moveObj[random].direction=="left"){
  267.                         xy=t.position[t.index]._x*t.cWidth;
  268.                         t.touchChild.style.cssText='transition-duration:0ms;-webkit-transition-duration:0ms;z-index:0;left:'+xy+'px;top:'+t.current._y+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
  269.                     }else{   
  270.                         xy=t.position[t.index]._y*t.cHeight;
  271.                         t.touchChild.style.cssText='transition-duration:0ms;-webkit-transition-duration:0ms;z-index:0;left:'+t.current._x+'px;top:'+xy+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
  272.                     }
  273.                     //交换移动后的坐标
  274.                     var aa=t.positionM[t.position[t.index]._index];
  275.                     //console.log("空"+t.positionM[t.position[t.index]._index]+"换"+_index+","+_index+"换"+aa);
  276.                     t.positionM[t.position[t.index]._index]=t.positionM[_index];
  277.                     t.positionM[_index]=aa;   
  278.                     //交换坐标
  279.                     t.position[moveObj[random].index]._x=t.position[t.index]._x;
  280.                     t.position[moveObj[random].index]._y=t.position[t.index]._y;
  281.                     t.position[moveObj[random].index]._index=t.position[t.index]._index;
  282.                     t.position[t.index]._x=_x;
  283.                     t.position[t.index]._y=_y;
  284.                     t.position[t.index]._index=_index;
  285.                 }
  286.                 t.answer.reverse();//=t.reverse(t.answer,t.answer.length-1,"");
  287.         },
  288.         //递归反转数组(不改变原数组),arr数组;length数组长度;str:""(空字符串)
  289.         reverse:function(arr,length,str){
  290.             var t=this;
  291.             return length==0?str+arr[0]:(t.reverse(arr,length-1,str+arr[length]+"=>"));
  292.         },
  293.         /**返回所有能移动的坐标信息
  294.         ***index:当前隐藏元素的下标
  295.         **/
  296.         getRadomPosition:function(index){
  297.             var t=this;
  298.             var arry=new Array();
  299.             if(t.position[index].y>0){
  300.                 arry.push({index:t.positionM[index-t.options.x],direction:"down"});
  301.             }
  302.             if(t.position[index].x>0){
  303.                 arry.push({index:t.positionM[index-1],direction:"right"});
  304.             }
  305.             if(t.position[index].x<t.options.x-1){
  306.                 arry.push({index:t.positionM[index+1],direction:"left"});
  307.             }
  308.             if(t.position[index].y<t.options.y-1){
  309.                 arry.push({index:t.positionM[index+t.options.x],direction:"up"});
  310.             }
  311.             return arry;
  312.         },
  313.         /**判断是否能移动
  314.         ***index:移动对象的下标
  315.         ***direction移动方向
  316.         **/
  317.         canMove:function(index,direction){
  318.             var t=this,_index=t.position[t.index]._index;
  319.             if((direction=="right"&&index==_index-1)||(direction=="left"&&index==_index+1)||(direction=="down"&&index==_index-t.options.x)||(direction=="up"&&index==_index+t.options.x)){
  320.                 return true;
  321.             }
  322.             return false;
  323.         },
  324.         extend:function(target,source){//拷贝不引用,改变拷贝的数组不会改变原数组
  325.             var t=this;
  326.             for (var p in source){
  327.                 if(t.getType(source[p])=="array"||t.getType(source[p])=="object"){
  328.                     target[p]=t.getType(source[p])=="array"?[]:{};
  329.                     arguments.callee(target[p],source[p]);
  330.                 }else{
  331.                     target[p] = source[p];
  332.                 }
  333.             }
  334.             return target;
  335.         },
  336.         getType:function(o)
  337.         {
  338.             var _t;
  339.             return ((_t = typeof(o)) == "object" ? o==null && "null" || Object.prototype.toString.call(o).slice(8,-1):_t).toLowerCase();
  340.         },
  341.         getId:function(elemId){return document.getElementById(elemId);},
  342.         getStyles:function(obj,name){
  343.             if(window.getComputedStyle){
  344.                 var getStyles;
  345.                 if ( obj.ownerDocument.defaultView.opener ) {
  346.                     var computed =obj.ownerDocument.defaultView.getComputedStyle( obj, null );
  347.                     getStyles= computed.getPropertyValue(name)||computed[ name];
  348.                 }else{
  349.                     var computed =window.getComputedStyle( obj, null);
  350.                     getStyles= computed.getPropertyValue(name)||computed[ name ];
  351.                 }
  352.             }else{
  353.                 getStyles=obj.currentStyle[name];
  354.             }
  355.             if(name=="width"){
  356.                 var maxWidth=arguments.callee(obj,"max-width");
  357.                 var pmaxWidth=parseFloat(maxWidth)||0;
  358.                 var pgetStyles=parseFloat(getStyles)||0;
  359.                 if(pmaxWidth&&(pgetStyles>pmaxWidth||!pgetStyles)){
  360.                     getStyles=maxWidth;
  361.                 }
  362.             }else if(name=="height"){
  363.                 var maxHeight=arguments.callee(obj,"max-height");
  364.                 var pmaxHeight=parseFloat(maxHeight)||0;
  365.                 var pgetStyles=parseFloat(getStyles)||0;
  366.                 if(pmaxHeight&&(pgetStyles>pmaxHeight||!pgetStyles)){
  367.                     getStyles=maxHeight;
  368.                 }
  369.             }
  370.             return getStyles;
  371.         },
  372.         setCss:function(obj,styleObj){
  373.             var cssCode = '';
  374.             if(document.createStyleSheet)//兼容ie8不能动态加载css
  375.             {  
  376.                 var sheet = document.createStyleSheet();
  377.                 for (var c in styleObj){
  378.                     this.insertCssRule(sheet,c,styleObj[c]);
  379.                 }
  380.             }else{
  381.                 for (var c in styleObj){
  382.                     cssCode+=c+'{'+styleObj[c]+'}';
  383.                 }
  384.                 var styleElement = document.createElement('STYLE');
  385.                 styleElement.type = 'text/css';
  386.                 var innerHTML = document.createTextNode(cssCode);
  387.                 styleElement.appendChild(innerHTML);
  388.                 if(obj.hasChildNodes()){
  389.                     obj.insertBefore(styleElement,obj.children[0]);
  390.                 }else if(obj){
  391.                     obj.appendChild(styleElement);
  392.                 }else{
  393.                     document.head.appendChild(styleElement);
  394.                 }
  395.             }   
  396.         },
  397.         insertCssRule:function(sheet,selectorText,cssText, position) {
  398.             position=position||0;
  399.             if (sheet.insertRule) {
  400.                 sheet.insertRule(selectorText + "{" + cssText + "}", position);
  401.             } else if (sheet.addRule) {
  402.                 sheet.addRule(selectorText, cssText, position);
  403.             }
  404.         },
  405.         addClass:function(o,cn){var re = new RegExp("(\\s*|^)"+cn+"\\b","g");o.className +=o.className?(re.test(o.className)?"":" "+ cn):cn;},
  406.         removeClass:function(o,cn){var re = new RegExp("(\\s*|^)"+cn+"\\b","g");var sName = o.className;o.className = sName.replace(re,"");}
  407.     }
  408. })(window);
复制代码

相关帖子

发表于 2017-1-10 15:16:33 来自手机 | 显示全部楼层
还是挺有借鉴意义的
使用道具 举报

回复

发表于 2017-1-11 08:46:24 | 显示全部楼层
js是要逆天的节奏js虽然不错,但是天生也是有缺陷,局限性。。。
使用道具 举报

回复

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

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部