什么是拖拽交换位置?看下图就明了,我们拖动里面的某个灰色方块,然后与其他灰色方块相互碰撞,如果那个方块离它的位置最近,则一开始那个方块出现红色虚线边框,松开鼠标然后这两者就进行位置互换。
对于这种效果,看似有些复杂。不过再复杂的效果还是由许多简单的事件构成的。以下就此效果做分部解析:
第一步 布局
或者对于你而言,这样的布局是简单的。但是如果需要让方块能够被拖拽,恐怕必须是绝对定位才能完成。而且在未知区域里方块的个数,你是无法对每个去计算他们对应的left和top值的,这个时候我们必须通过js来完成这种布局上的转换。
首先放上css和html代码:
<style> ul li{list-style:none;} #box{width:300px;margin:0 auto;} #box li{width:80px;height:80px;float:left;margin:10px;background:#ccc;line-height:80px;text-align:center;display:inline;cursor:pointer;} #box .act{border:1px dashed red;} </style>
<ul id=”box”> <li>111</li> <li>222</li> <li>333</li> <li>444</li> <li>555</li> <li>666</li> <li>777</li> <li>888</li> <li>999</li> </ul>
|
然后对于列表进行布局转换:
var oBox=document.getElementById("box"), aLi=oBox.getElementsByTagName("li"), i=0, len=aLi.length, aPos=[];
for(i=0;i<len;i++){ aPos[i]={left:aLi[i].offsetLeft,top:aLi[i].offsetTop}; }
for(i=0;i<len;i++){ aLi[i].style.left=aPos[i].left+"px"; aLi[i].style.top=aPos[i].top+"px"; aLi[i].style.position="absolute"; aLi[i].style.margin=0; }
|
此时每个li都有对应的left和top值,如下图:
第二步 对每个方块进行拖拽操作
关于拖拽,有以下代码:
function drag(obj){ var iMinZindex=100 obj.onmousedown=function(ev){ clearInterval(obj.timer); iMinZindex++; obj.style.zIndex=iMinZindex; var e=ev||event; var disX=e.clientX-obj.offsetLeft; var disY=e.clientY-obj.offsetTop; if(obj.setCapture){ obj.onmousemove=fnMove; obj.onmouseup=fnUp; obj.setCapture(); } else{ document.onmousemove=fnMove; document.onmouseup=fnUp; } function fnMove(ev){ var e=ev||event; x=e.clientX-disX; y=e.clientY-disY; obj.style.left=x+"px"; obj.style.top=y+"px"; } function fnUp(){ this.onmousemove=null; this.onmouseup=null; if(this.setCapture){ this.releaseCapture(); } } return false; } }
for(var i=0;i<len;i++){ drag(aLi[i]); }
|
此时,每个li都可以进行随意的拖动了,并且每拖动一次,li的zIndex就会加1,所以,最后一个拖动的li层级总是最高的。
第三步 两个方块碰撞的条件?
假设碰撞的两个方块分别是obj1和obj2,obj1是被拖拽的方块,那么怎么样才能判断它们两个是否碰撞了呢?这里我们给出了一个反向思路,即obj1和obj2不碰撞的产生条件是什么?
- obj1的右边框要小于obj2的左边框
2.obj1的左边框要大于obj2的右边框
3.obj1的下边框要小于obj2的上边框
4.obj1的上边框要大于obj2的下边框
以上四种情况均可以使得obj1和obj2不碰撞。于是我们得到函数delect:
function delect(obj1,obj2){ var l1=obj1.offsetLeft, r1=obj1.offsetLeft+obj1.offsetWidth, t1=obj1.offsetTop, b1=obj1.offsetTop+obj1.offsetHeight, l2=obj2.offsetLeft, r2=obj2.offsetLeft+obj2.offsetWidth, t2=obj2.offsetTop, b2=obj2.offsetTop+obj2.offsetHeight; if(r1<l2||l1>r2||b1<t2||t1>b2){ return false; } else{ return true; } }
|
这样我们通过delect函数的返回值,就能知道两个物体是否发生了碰撞。
第四步 当同时与多个方块碰撞,求最短距离,并得到最短距离的方块
首先,当被拖拽的物体同时与几个方块相互碰撞时,则我们首先求出这些方块与被拖拽方块的距离,距离函数如下:
function dist(obj1,obj2){ var a=obj1.offsetLeft-obj2.offsetLeft, b=obj1.offsetTop-obj2.offsetTop; return Math.sqrt(a*a+b*b); }
|
再对这些方块的距离进行比较,从而返回那个与它位置最近的方块。
function shortestDis(obj){ var iNow=-1,iMin=99999; for(var i=0;i<len;i++){ if(obj==aLi[i])continue; if(delect(obj,aLi[i])){ var dis=dist(obj,aLi[i]); if(dis<iMin){ iMin=dis; iNow=i; } } } if(iNow==-1){ return null; } else{ return aLi[iNow]; } }
|
此时,通过这个函数我们得到了最短距离的方块,我们需要给它加一个红色边框,而这些操作都是拖动的过程中完成的,于是我们回到拖动的过程中,有:
function fnMove(ev){ var e=ev||event; x=e.clientX-disX; y=e.clientY-disY; obj.style.left=x+"px"; obj.style.top=y+"px";
for(var i=0;i<len;i++){ aLi[i].className=""; }
var near=shortestDis(obj);
if(near){ near.className="act"; } }
|
第五步 两个碰撞的方块进行位置互换
虽然我们知道交换位置是在释放鼠标时完成的。但是我们并不知道被我们拖拽的方块的索引值,这样我们就无法对这两个方块的进行位置赋值,怎么办呢?如果我们分别知道这被拖动的方块和距离最近的方块的索引值,再依据之前提到的aPos[i],那么这两个方块的位置就可以确定了,于是我们在开始循环的时候,给每个li加索引值:
for(i=0;i<len;i++){ aLi[i].style.left=aPos[i].left+"px"; aLi[i].style.top=aPos[i].top+"px"; aLi[i].style.position="absolute"; aLi[i].style.margin=0; aLi[i].index=i; }
function fnUp(){ this.onmousemove=null; this.onmouseup=null; var near=shortestDis(obj); if(near){ obj.style.left=aPos[near.index].left+"px"; obj.style.top=aPos[near.index].top+"px"; near.style.left=aPos[obj.index].left+"px"; near.style.top=aPos[obj.index].top+"px"; near.className=""; near.style.zIndex=iMinZindex; var temp=obj.index; obj.index=near.index; near.index=temp; } else{ obj.style.left=aPos[obj.index].left+"px"; obj.style.top=aPos[obj.index].top+"px"; } if(this.setCapture){ this.releaseCapture(); } }
|
整个效果就是以上的五个步骤。完整代码如下:
(function(){ var oBox=document.getElementById("box"),aLi=oBox.getElementsByTagName("li"),i=0,len=aLi.length,aPos=[]; for(i=0;i<len;i++){ aPos[i]={left:aLi[i].offsetLeft,top:aLi[i].offsetTop}; } for(i=0;i<len;i++){ aLi[i].style.left=aPos[i].left+"px"; aLi[i].style.top=aPos[i].top+"px"; aLi[i].style.position="absolute"; aLi[i].style.margin=0; aLi[i].index=i; } function drag(obj){ var iMinZindex=100 obj.onmousedown=function(ev){ clearInterval(obj.timer); iMinZindex++; obj.style.zIndex=iMinZindex; var e=ev||event; var disX=e.clientX-obj.offsetLeft; var disY=e.clientY-obj.offsetTop; if(obj.setCapture){ obj.onmousemove=fnMove; obj.onmouseup=fnUp; obj.setCapture(); } else{ document.onmousemove=fnMove; document.onmouseup=fnUp; } function fnMove(ev){ var e=ev||event; x=e.clientX-disX; y=e.clientY-disY; obj.style.left=x+"px"; obj.style.top=y+"px"; for(var i=0;i<len;i++){ aLi[i].className=""; } var near=shortestDis(obj); if(near){ near.className="act"; } document.title=obj.index; } function fnUp(){ this.onmousemove=null; this.onmouseup=null; var near=shortestDis(obj); if(near){ obj.style.left=aPos[near.index].left+"px"; obj.style.top=aPos[near.index].top+"px"; near.style.left=aPos[obj.index].left+"px"; near.style.top=aPos[obj.index].top+"px"; near.className=""; near.style.zIndex=iMinZindex; var temp=obj.index; obj.index=near.index; near.index=temp; } else{ obj.style.left=aPos[obj.index].left+"px"; obj.style.top=aPos[obj.index].top+"px"; } if(this.setCapture){ this.releaseCapture(); } } return false; } } for(var i=0;i<len;i++){ drag(aLi[i]); } function delect(obj1,obj2){ var l1=obj1.offsetLeft, r1=obj1.offsetLeft+obj1.offsetWidth, t1=obj1.offsetTop, b1=obj1.offsetTop+obj1.offsetHeight, l2=obj2.offsetLeft, r2=obj2.offsetLeft+obj2.offsetWidth, t2=obj2.offsetTop, b2=obj2.offsetTop+obj2.offsetHeight; if(r1<l2||l1>r2||b1<t2||t1>b2){ return false; } else{ return true; } } function dist(obj1,obj2){ var a=obj1.offsetLeft-obj2.offsetLeft, b=obj1.offsetTop-obj2.offsetTop; return Math.sqrt(a*a+b*b); } function shortestDis(obj){ var iNow=-1,iMin=99999; for(var i=0;i<len;i++){ if(obj==aLi[i])continue; if(delect(obj,aLi[i])){ var dis=dist(obj,aLi[i]); if(dis<iMin){ iMin=dis; iNow=i; } } } if(iNow==-1){ return null; } else{ return aLi[iNow]; } } })()
|