前面的話
前面分別介紹了拖拽模擬和磁性吸附,當可視區域內存在多個可拖拽元素,就出現碰撞檢測的問題,這也是javascript動畫的一個經典問題。本篇將詳細介紹碰撞檢測
原理介紹
碰撞檢測的方法有很多,接下來使用九宮格分析法
假設黃色元素要與紅色元素進行碰撞。將紅色元素所處的區域分為9部分,自身處於第9部分,周圍還存在8個部分。只要黃色元素進入紅色元素的第9部分,就算碰撞。否則,都算未碰撞

總共分為以下5種情況:
1、處於上側未碰撞區域——1、2、3區域
2、處於右側未碰撞區域——3、4、5區域
3、處於下側未碰撞區域——5、6、7區域
4、處於左側未碰撞區域——1、7、8區域
5、處於碰撞區域——9區域
代碼實現
我們把上面的原理用代碼實現
function bump(obj,objOther,bgColor){ /***被碰元素***/ //被碰元素左側距離可視區域左側的距離 var L0 = obj.offsetLeft; //被碰元素上側距離可視區域上側的距離 var T0 = obj.offsetTop; //被碰元素右側距離可視區域右側的距離 var R0 = obj.offsetLeft + obj.offsetWidth; //被碰元素下側距離可視區域下側的距離 var B0 = obj.offsetTop + obj.offsetHeight; /**侵入元素**/ var L = objOther.offsetLeft; var T = objOther.offsetTop; var R = objOther.offsetLeft + objOther.offsetWidth; var B = objOther.offsetTop + objOther.offsetHeight; /*******碰撞檢測*******/ //上側區域if(B < T0) //左側區域if(R < L0) //右側區域if(L > R0) //下側區域if(T > B0) //碰撞區域 if(B >= T0 && R >= L0 && L <= R0 && T <= B0){ obj.style.backgroundColor = 'red'; }else{ obj.style.backgroundColor = bgColor; } }
完整效果
<div id="test1" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;">元素一</div> <div id="test2" style="height: 100px;width: 100px;background:orange;position:absolute;top:150px;left:150px;">元素二</div> <script> function addEvent(target,type,handler){ if(target.addEventListener){ target.addEventListener(type,handler,false); }else{ target.attachEvent('on'+type,function(event){ return handler.call(target,event); }); } } function getCSS(obj,style){ if(window.getComputedStyle){ return getComputedStyle(obj)[style]; } return obj.currentStyle[style]; } function bump(obj,objOther){ bump.objBGColor = (bump.objBGColor === undefined) ? getCSS(obj,'backgroundColor') : bump.objBGColor; bump.objOtherBGColor = (bump.objOtherBGColor === undefined) ? getCSS(objOther,'backgroundColor') : bump.objOtherBGColor; /***被碰元素***/ //被碰元素左側距離可視區域左側的距離 var L0 = obj.offsetLeft; //被碰元素上側距離可視區域上側的距離 var T0 = obj.offsetTop; //被碰元素右側距離可視區域右側的距離 var R0 = obj.offsetLeft + obj.offsetWidth; //被碰元素下側距離可視區域下側的距離 var B0 = obj.offsetTop + obj.offsetHeight; /**侵入元素**/ var L = objOther.offsetLeft; var T = objOther.offsetTop; var R = objOther.offsetLeft + objOther.offsetWidth; var B = objOther.offsetTop + objOther.offsetHeight; /*******碰撞檢測*******/ //上側區域if(B < T0) //左側區域if(R < L0) //右側區域if(L > R0) //下側區域if(T > B0) //碰撞區域 if(B >= T0 && R >= L0 && L <= R0 && T <= B0){ obj.style.backgroundColor = objOther.style.backgroundColor ='red'; }else{ obj.style.backgroundColor = bump.objBGColor; objOther.style.backgroundColor = bump.objOtherBGColor; } } function drag(ele){ var x0,y0,x1,y1,isMoving; var L0,R0,T0,B0,EH,EW; var mousedownHandler = function(e){ e = e || event; //獲取元素距離定位父級的x軸及y軸距離 x0 = this.offsetLeft; y0 = this.offsetTop; //獲取此時鼠標距離視口左上角的x軸及y軸距離 x1 = e.clientX; y1 = e.clientY; //按下鼠標時,表示正在運動 isMoving = true; //鼠標按下時,獲得此時的頁面區域 L0 = 0; R0 = document.documentElement.clientWidth; T0 = 0; B0 = document.documentElement.clientHeight; //鼠標按下時,獲得此時的元素寬高 EH = ele.offsetHeight; EW = ele.offsetWidth; } var mousemoveHandler = function(e){ //如果沒有觸發down事件,而直接觸發move事件,則函數直接返回 if(!isMoving){ return; } e = e || event; //獲取此時鼠標距離視口左上角的x軸及y軸距離 var x2 = e.clientX; var y2 = e.clientY; //計算此時元素應該距離視口左上角的x軸及y軸距離 var X = x0 + (x2 - x1); var Y = y0 + (y2 - y1); /******范圍限定*******/ //獲取鼠標移動時元素四邊的瞬時值 var L = X; var R = X + EW; var T = Y; var B = Y + EH; //在將X和Y賦值給left和top之前,進行范圍限定。只有在范圍內時,才進行相應的移動 //如果脫離左側范圍,則left置L0 if(L < L0){X = L0;} //如果脫離右側范圍,則left置為R0 if(R > R0){X = R0 - EW;} //如果脫離上側范圍,則top置T0 if(T < T0){Y = T0;} //如果脫離下側范圍,則top置為B0 if(B > B0){Y = B0 - EH;} //將X和Y的值賦給left和top,使元素移動到相應位置 ele.style.left = X + 'px'; ele.style.top = Y + 'px'; bump(test2,test1); } var mouseupHandler = function(e){ //鼠標抬起時,表示停止運動 isMoving = false; //釋放全局捕獲 if(ele.releaseCapture){ ele.releaseCapture(); } } var preventDefaultHandler = function(e){ e = e || event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue = false; } //IE8-瀏覽器阻止默認行為 if(ele.setCapture){ ele.setCapture(); } } addEvent(ele,'mousedown',mousedownHandler); addEvent(ele,'mousedown',preventDefaultHandler); addEvent(document,'mousemove',mousemoveHandler) addEvent(document,'mouseup',mouseupHandler) }; drag(test1); drag(test2); </script>