• js原生图片拼图Demo


    自从找着了工作后已经有将近三个月没有写博客啦。最近在加强js的逻辑练习,所以收集了一些js小练习,手把手把它敲出来了。我把它记录下来,我的学习分享,嘿嘿。。。

    贴下界面:

                                          图一  界面

    这个拼图游戏主要分为几个步骤:

    (1)小图片列表切换,点击显示相应的大图(大图是由20张小图片拼连起来的)

    (2)点击开始游戏后,20张小图片随机排序

    (3)此时鼠标移到大图上显示移动状态,可以拖动,开始记录时间

    (4)碎片拼成原图后显示拼图成功以及所花的时间

    下面我来分解一下流程:

    (1)先展示html及css的代码:

     1 <!DOCTYPE html>
     2 <html>
     3     <head>
     4         <meta charset="utf-8">
     5         <title>拼图游戏</title>
     6         <style>
     7             body,ul,li{margin:0;padding:0;text-align: center;}
     8             body{font:30px/1.5 Tahoma;}
     9             #box{position:relative;width:1024px;height:768px;margin:10px auto;background:#808080;}
    10             #box li{float:left;width:256px;height:154px;overflow:hidden;}
    11             #box li img{width:256px;height:154px;}
    12             #box li.hig{width:256px;height:154px;overflow:hidden;border:2px dashed yellow;}
    13             #box li.hig img{width:256px;height:154px;opacity:0.5;filter:alpah(opacity=50);}
    14             #mask{position:absolute;top:0;left:0;width:1024px;height:768px;background:red;opacity:0;filter:alpha(opacity=0);}
    15             input{cursor:pointer;}
    16             #photo{text-align:center;margin:10px 0;}
    17             #photo img{width:100px;height:auto;border-radius:5px;margin:0 5px;opacity:0.5;filter:alpha(opacity=50);cursor:pointer;}
    18             #photo img.hover{opacity:1;filter:alpha(opacity=100);}
    19             #photo img.selected{border:2px solid yellow;width:96px;height:auto;opacity:1;filter:alpha(opacity=100);}
    20         </style>
    21     </head>
    22     <body>
    23         <div id="photo"><img src="img/flower/img_01.jpg" class="selected" /><img src="img/flower/img_02.jpg" /><img src="img/flower/img_03.jpg" /></div>
    24         <input type="button" value="开始游戏" id="play-btn"/>
    25         <ul id="box"></ul>
    26 </body>
    27 </html>
    View Code

    首先实现如下的效果:

    说明:鼠标悬浮到小图片上,图片透明度变为50%,点击图片,产生黄色边框,下面的大图也跟着变化,点击开始按钮,内容会变成重新开始

    js代码:

     1 var oPhoto=document.getElementById("photo");
     2 var aThumbImg=oPhoto.getElementsByTagName('img');
     3 var oStartBtn=document.getElementById("play-btn");
     4 var oBox=document.getElementById("box");
     5 var isRandom=false;
     6 var dirPath=0;
    var odata=[];
    7 for(var i=0;i<20;i++){ 8 var temp=i+1; 9 odata.push(temp); 10 } 11 for(var i=0;i<aThumbImg.length;i++){ 12 aThumbImg[i].index=i; 13 } 14 oPhoto.onmouseover=function(ev){ 15 var ev = ev || window.event; 16 var target = ev.target || ev.srcElement; 17 if(target.nodeName.toLowerCase()=='img'){ 18 target.className+=' hover'; 19 } 20 } 21 oPhoto.onmouseout=function(ev){ 22 var ev = ev || window.event; 23 var target = ev.target || ev.srcElement; 24 if(target.nodeName.toLowerCase()=='img'){ 25 target.className=target.className.replace(/shover/,''); 26 } 27 } 28 oPhoto.onclick=function(ev){ 29 var ev = ev || window.event; 30 var target = ev.target || ev.srcElement; 31 if(target.nodeName.toLowerCase()=='img'){ 32 for(var i=0;i<aThumbImg.length;i++){ 33 aThumbImg[i].className=''; 34 } 35 target.className='selected'; 36 dirPath=target.index; 37 oStartBtn.value='开始游戏'; 38 oBox.innerHTML="" 39 odata.sort(function(a,b){ 40 return a-b; 41 }); 42 43 } 44 } 45 oStartBtn.onclick=function(){ 46 this.value="重新开始"; 47 oBox.innerHTML=""; 48 }

    上面都是比较简单的,这个游戏的核心还是在于边界判断,移动上。

    (2)现在来实现拖动这一部分,先讲解onmousedown和onmousemove事件

    2.1 首先,还未开始游戏时,我设置了一层遮罩,就是不让其拖动,所以我们来建个遮罩层,也是放在id='box'里面,跟li同级。

    oPhoto.onclick=function(ev){
        creatMask();
    }
    
    function creatMask(){
        var mask=document.createElement('div');
        mask.id='mask';
        mask.style.zIndex=zIndex;
        oBox.appendChild(mask);                    
    }
    creatMask();

    当进入页面是生成遮罩层,点击小图片时,遮罩层还存在,只有点击开始游戏时,才会移除遮罩层。这时候,图片切块才能开始拖动。

    2.2 点击开始游戏,给图片切块注册onmousedown和onmousemove事件

     1 function drag(obj){
     2   obj.style.cursor='move';                      
    obj.onmousedown=function(event){ 3 var event=event||window.event; 4 event.preventDefault(); 5 var start_x=event.clientX-this.offsetLeft; 6 var start_y=event.clientY-this.offsetTop; 7 obj.style.zIndex = zIndex++;
        document.onmousemove=function(event){ 8 var event=event||window.event; 9 event.preventDefault(); 10 var iL=event.clientX-start_x; 11 var iT=event.clientY-start_y; 12 var maxL=obj.parentNode.clientWidth-obj.offsetWidth; 13 var maxT=obj.parentNode.clientHeight-obj.offsetHeight; 14 //边界判断 15 if(iL<0){iL=0;} 16 if(iT<0){iT=0;} 17 if(iL>maxL){iL=maxL;} 18 if(iT>maxT){iT=maxT;} 19 obj.style.left=iL+'px'; 20 obj.style.top=iT+'px'; 22 } 23 } 24 }

    上面的代码主要实现了图片拖动,且拖动范围不能超过box的范围。

    好了,现在要实现拖动的小块与周边的切片的碰撞检测,与之最近的切块显示黄色边框

    效果:

    这其中的原理是:  查找拖动块附近的最近的li,然后给li容器添加黄色边线。

    js代码:

    function findNear(obj){
                            var minLi=null;
                            var minNum=Number.MAX_VALUE;
                            var filterLi=[];
                            var aDistance=[];
                            for(var i=0;i<aLi.length;i++){
                                if(aLi[i]!=obj&&isButt(obj,aLi[i])){                                
                                    aDistance.push(getDistance(obj,aLi[i]));
                                    filterLi.push(aLi[i]);
                                }
                            }
                            var minNum=Number.MAX_VALUE;
                            //遍历查询最小值
                            for(var i=0;i<aDistance.length;i++){
                                if(aDistance[i]<minNum){
                                    minNum=aDistance[i];
                                    minLi=filterLi[i];
                                }
                            }
                            return minLi;
                        }    
    function isButt(obj1,obj2){
                        var l1=obj1.offsetLeft;
                        var lw1=obj1.offsetWidth+l1;
                        var t1=obj1.offsetTop;
                        var th1=obj1.offsetTop+t1;
                        
                        var l2=obj2.offsetLeft;
                        var lw2=obj2.offsetWidth+l2;
                        var t2=obj2.offsetTop;
                        var th2=obj2.offsetTop+t2;
                        //上下左右判断
                        return !(th2<t1||th1<t2||lw2<l1||lw1<l2);
                    }
                    function getDistance(obj1,obj2){
                        var dir_x=(obj1.offsetLeft+obj1.offsetWidth/2)-(obj2.offsetLeft+obj2.offsetWidth/2);
                        var dir_y=(obj1.offsetTop+obj1.offsetHeight/2)-(obj2.offsetTop+obj2.offsetHeight/2);
                        var dir=Math.sqrt(dir_x*dir_x+dir_y*dir_y);
                        return dir;
                    }            

    然后在onmousedown里面调用

    var oNear=null;
    for (i = 0; i < aLi.length; i++) aLi[i].className = "";
          oNear=findNear(obj);//返回一个最靠近obj的块
        //对最近的块显示黄色边框
          if(oNear){
               oNear.className='hig';
         }

    边界判断、isButt()函数、 findNear()函数和的分析图:

    (3)实现了移动拖动这一部分,那么鼠标释放时,又怎么实现拖动块与最近切片的位置交换呢。

    首先,得先清楚一点,取个例子:

    <ul id="box">
       <li>1</li>
       <li>2</li>
       <li>3</li>
    </ul>

    不管你怎么拖动里标签,改变的只是他的left和top,li标签在文档的先后顺序是不会改变的。那我们来看看li标签拖动过程的变化,如下图:

     

    js代码:

    document.onmouseup=function(event){
         document.onmousemove = null;//移除事件
         document.onmouseup = null;
         var event=event||window.event;
         event.preventDefault();
         //鼠标释放后碎片与目标碎片对换
         if(oNear){
            var obj_index=obj.index;
            obj.index=oNear.index;
            oNear.index=obj_index;
           //obj移动到oNear的位置
            startMove(obj,oPos[obj.index]);
            //oNear移动到obj的位置
            startMove(oNear,oPos[oNear.index],function(){
                //判断碎片是否还原成功
         //      console.log(finish());
                if(finish()){
                   alert('恭喜你,拼图游戏完美成功!!!');
                   creatMask();
                }                                        
            });
            oNear.className="";//移除黄色边框
         }else{
             //回到原位
             startMove(obj,oPos[obj.index]);
         }
    }
    function startMove(obj,pos,onEnd){ obj.timer=setInterval(function(){ doMove(obj,pos,onEnd); },30); }
    function doMove(obj,pos,onEnd){ var curOffsetLeft=obj.offsetLeft; var curOffsetTop=obj.offsetTop; //下面设置图片碎片每次向目标点移动的位移 var speedL=(pos.left-curOffsetLeft)/5; var speedT=(pos.top-curOffsetTop)/5; speedL=speedL>0 ? Math.ceil(speedL) : Math.floor(speedL); speedT=speedT>0 ? Math.ceil(speedT) : Math.floor(speedT); obj.style.left=(curOffsetLeft+speedL)+'px'; obj.style.top=(curOffsetTop+speedT)+'px'; //当obj的left等于对换oNear所保存的left,说明已经和它完全吻合了。此时移除计时器。 if(curOffsetLeft==pos.left && curOffsetTop==pos.top){ clearInterval(obj.timer); //如果传入回调函数,则执行回调函数 onEnd && onEnd(); // return; } } function finish(){ var success=true; var tempArr=[]; tempArr.length=0; for(var i=0;i<aLi.length;i++){ for(var j=0;j<aLi.length;j++){ if(i==aLi[j]['index']){ //li标签内图片的序号添加进数组 var img=aLi[j].getElementsByTagName('img')[0]; //返回匹配到的子集元素 var srcIndex=img.src.match(//(d+).jpg/)[1]; // console.log(srcIndex); tempArr.push(srcIndex); } } } for(var i=1;i<=tempArr.length;i++){ if(i!=tempArr[i-1]){ success=false; break; } } return success;
    }

    好啦,整个过程就是这样子。拿来练手还是不错的。

    demo中的图片碎片是我原先切好的,其实我本来是想将原图用程序裁切的,奈何技术不够,所以只能用ps处理了一下,这是我觉得比较遗憾的事啦,有兴趣的你可以实现一下呗。。。

    这里是demo的源代码地址:https://coding.net/u/U_can/p/puzzleGame/git

    
    
  • 相关阅读:
    如何定义自定义消息
    AfxBeginThread中使用updatedata出错
    ThinkPHP 3.2.3 简单后台模块开发(二)RBAC
    python 排序算法总结及实例详解
    python 排序算法总结及实例详解
    Excel 2016在大数据分析领域有了很多的改善
    Excel 2016在大数据分析领域有了很多的改善
    朴素贝叶斯算法的python实现方法
    朴素贝叶斯算法的python实现方法
    大数据面临的三重困难需从四个方面优化
  • 原文地址:https://www.cnblogs.com/U-can/p/4975335.html
Copyright © 2020-2023  润新知