js原生圖片拼圖Demo

自從找着了工做後已經有將近三個月沒有寫博客啦。最近在增強js的邏輯練習,因此收集了一些js小練習,手把手把它敲出來了。我把它記錄下來,個人學習分享,嘿嘿。。。css

貼下界面:html

                                      圖一  界面node

這個拼圖遊戲主要分爲幾個步驟:git

(1)小圖片列表切換,點擊顯示相應的大圖(大圖是由20張小圖片拼連起來的)數組

(2)點擊開始遊戲後,20張小圖片隨機排序app

(3)此時鼠標移到大圖上顯示移動狀態,能夠拖動,開始記錄時間dom

(4)碎片拼成原圖後顯示拼圖成功以及所花的時間ide

下面我來分解一下流程:函數

(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

相關文章
相關標籤/搜索