文章出處

前面的話

  碰撞可以分為碰壁和互碰兩種形式,上篇介紹了碰壁運動,本文將從淺入深地介紹碰撞運動的互碰形式

 

碰撞檢測

  對于互碰形式的碰撞運動來說,首先要解決的是碰撞檢測。對于矩形元素的碰撞檢測前面的博文已經詳細介紹過,下面主要介紹圓形元素的碰撞檢測

  矩形元素的碰撞檢測利用九宮格分析法,而圓形元素的碰撞檢測則簡單很多,判斷兩個圓形元素的半徑之和是否大于兩個圓形元素的圓心點坐標之間的距離即可

  由示意圖可知,元素一的圓心位置為(x1,y1),半徑為r1,元素二的圓心位置為(x2,y2),半徑為r2

  兩個元素圓心之間的距離len = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))

  當len<= r1+r2時,說明兩個圓形元素發生碰撞

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div id="test1" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;border-radius: 50%;"></div>
<div id="test2" style="height: 200px;width: 200px;background:orange;position:absolute;top:150px;left:150px;border-radius: 50%;"></div>
<script>
function getCSS(obj,style){
    if(window.getComputedStyle){
        return getComputedStyle(obj)[style];
    }
    return obj.currentStyle[style];
} 
function bump(obj,objOther,bgColor){

    /***被碰元素***/
    var r1 = obj.offsetWidth/2;
    var x1 = parseFloat(getCSS(obj,'left')) + r1;
    var y1 = parseFloat(getCSS(obj,'top')) + r1;
    /**侵入元素**/
    var r2 = objOther.offsetWidth/2;
    var x2 = parseFloat(getCSS(objOther,'left')) + r2;
    var y2 = parseFloat(getCSS(objOther,'top')) + r2;
    //碰撞檢測
    var len = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));

    if(len <= r1 + r2){
        obj.style.backgroundColor = 'red';
    }else{
        obj.style.backgroundColor = bgColor;
    }
}


function drag(obj){
    obj.onmousedown = function(e){
        e = e || event;
        //提升當前元素的層級
        obj.style.zIndex = '1';
        //獲取元素距離定位父級的x軸及y軸距離
        var x0 = this.offsetLeft;
        var y0 = this.offsetTop;
        //獲取此時鼠標距離視口左上角的x軸及y軸距離
        var x1 = e.clientX;
        var y1 = e.clientY;
        //鼠標按下時,獲得此時的頁面區域
        var L0 = 0;
        var R0 = document.documentElement.clientWidth;
        var T0 = 0;
        var B0 = document.documentElement.clientHeight;
        //鼠標按下時,獲得此時的元素寬高
        var EH = obj.offsetHeight;
        var EW = obj.offsetWidth;
        document.onmousemove = function(e){
            e = e || event;
            //獲取此時鼠標距離視口左上角的x軸及y軸距離
            x2 = e.clientX;
            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;}
            obj.style.left = X + 'px';
            obj.style.top = Y + 'px';
            //運行碰撞檢測函數
            bump(test2,test1,'orange')
        }
        document.onmouseup = function(e){
            //降低當前元素的層級
            obj.style.zIndex = '0';
            //當鼠標抬起時,拖拽結束,則將onmousemove賦值為null即可
            document.onmousemove = null;
            //釋放全局捕獲
            if(obj.releaseCapture){
                obj.releaseCapture();
            }
        }
        //阻止默認行為
        return false;
        //IE8-瀏覽器阻止默認行為
        if(obj.setCapture){
            obj.setCapture();
        }
    }    
}
drag(test1);
drag(test2);
</script>      
</body>
</html>

無損碰撞

  假設兩個元素的碰撞,對元素的速度并不產生損耗,而只是改變元素速度方向

  假設元素一在與元素二碰撞前的瞬時速度是v,將該速度分解為平行于碰撞方向的速度v1和垂直于碰撞方向的速度v2

  碰撞發生后,碰撞方向的速度v1變成了反向的v1

  將反向的v1分解到水平方向v1x和垂直方向v1y

  將垂直于碰撞方向的速度v2分解到水平方向v2x和垂直方向v2y

    水平方向的速度vx = v2x - v1x
    垂直方向的速度vy = v2y - v1y

  元素二的速度分解形式與元素一類似,就不再贅述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<button id="btn1">開始運動</button>
<button id="reset">還原</button>
<div id="test1" style="height: 150px;width: 150px;background:pink;position:absolute;top:50px;left:50px;border-radius: 50%;"></div>
<div id="test2" style="height: 150px;width: 150px;background:orange;position:absolute;top:250px;left:250px;border-radius: 50%;"></div>
<script>
//聲明元素的步長值
//步長值默認值為[-25,-20,-15,-10,-5,0,5,10,15,20]中的一個隨機數
test1.stepX =  5*Math.floor(Math.random() * 10 - 5);
test1.stepY =  5*Math.floor(Math.random() * 10 - 5);
test2.stepX =  5*Math.floor(Math.random() * 10 - 5);
test2.stepY =  5*Math.floor(Math.random() * 10 - 5);
btn1.onclick = function(){
    collisionMove({
        obj:test1
    })
    collisionMove({
        obj:test2
    })
}
reset.onclick = function(){
    history.go();
}
function collisionMove(json){
    var obj = json.obj;
    var fn = json.fn;
    //聲明x、y軸的當前值
    var curX,curY;
    //聲明x、y軸方向
    var dirX = json.dirX;
    var dirY = json.dirY;
    dirX = obj.stepX > 0 ? '+' : '-';
    dirY = obj.stepY > 0 ? '+' : '-';
    //聲明offset寬高
    var offsetWidth = obj.offsetWidth;
    var offsetHeight = obj.offsetHeight;
    //聲明元素活動區域寬高
    var activeWidth = json.activeWidth;
    var activeHeight = json.activeHeight;
    //元素獲取區域寬高默認值為可視區域寬高
    activeWidth = Number(activeWidth) || document.documentElement.clientWidth;
    activeHeight = Number(activeHeight) || document.documentElement.clientHeight;
    //聲明left、top樣式值
    var left,top;
    //清除定時器
    if(obj.timer){return;}
     //開啟定時器
    obj.timer = setInterval(function(){
        //獲取x、y軸的當前值
        curX = parseFloat(getCSS(obj,'left'));
        curY = parseFloat(getCSS(obj,'top'));
        bump(test1,test2);
        //更新left、top值
        left = curX + obj.stepX;
        top = curY + obj.stepY;
        //右側碰壁前一刻,步長大于剩余距離,且元素向右運動時
        if((left > activeWidth - offsetWidth) && (dirX == '+')){
            left = activeWidth - offsetWidth;
        }
        //左側碰壁前一刻,步長大于剩余距離,且元素向左運動時
        if((Math.abs(obj.stepX) > curX) && (dirX == '-')){
            left = curX;
        }
        //下側碰壁前一刻,步長大于剩余距離,且元素向下運動時
        if((top > activeHeight - offsetHeight) && (dirY == '+')){
            top = activeHeight - offsetHeight;
        }
        //上側碰壁前一刻,步長大于剩余距離,且元素向上運動時
        if((Math.abs(obj.stepY) > curY) && (dirY == '-')){
            top = curY;
        }
        obj.style.left= left + 'px';
        obj.style.top = top + 'px';
        //左側或右側碰撞瞬間
        if(left == activeWidth - offsetWidth || left == curX){
            obj.stepX = -obj.stepX;
        }
        //上側或下側碰撞瞬間
        if(top == activeHeight - offsetHeight || top == curY){
            obj.stepY = -obj.stepY;
        }
        //更新運動方向
        dirX = obj.stepX > 0 ? '+' : '-';
        dirY = obj.stepY > 0 ? '+' : '-';
    },20);            
}

function getCSS(obj,style){
    if(window.getComputedStyle){
        return getComputedStyle(obj)[style];
    }
    return obj.currentStyle[style];
} 
//碰撞檢測函數
function bump(obj,objOther){
    /***動態元素***/
    obj.r = obj.offsetWidth/2;
    obj.x0 = parseFloat(getCSS(obj,'left')) + obj.r;
    obj.y0 = parseFloat(getCSS(obj,'top')) + obj.r;
    /**靜態元素**/
    objOther.r = objOther.offsetWidth/2;
    objOther.x0 = parseFloat(getCSS(objOther,'left')) + objOther.r;
    objOther.y0 = parseFloat(getCSS(objOther,'top')) + objOther.r;
    //圓心之間的距離
    var len = Math.sqrt((obj.x0-objOther.x0)*(obj.x0-objOther.x0) + (obj.y0-objOther.y0)*(obj.y0-objOther.y0));
    //發生碰撞
    if(len <= obj.r + objOther.r){
    //碰撞方向與水平負方向的夾角a 
        var a = Math.atan(Math.abs((obj.y0-objOther.y0)/(obj.x0-objOther.x0)));
        stepChange(test1,test2,a);
        stepChange(test2,test1,a);
    }
}
//碰撞時,步長變化函數
function stepChange(obj,objOther,a){
    //步長合并
    obj.step = Math.sqrt(obj.stepX*obj.stepX + obj.stepY*obj.stepY);
    //假設總步長方向與x軸方向的夾角為b
    obj.b = Math.atan(Math.abs(obj.stepY/obj.stepX));
    //假設總步長方向與碰撞方向的夾角為c
    obj.c = Math.abs(a - obj.b);
    //步長分解
    //碰撞方向
    obj.step1 = obj.step*Math.cos(obj.c);
    //垂直方向
    obj.step2 = obj.step*Math.sin(obj.c);
    //按照運動元素(侵入元素)的起始運動方向對步長進行重新分解
    //左上
    if(obj.x0 <= objOther.x0 && obj.y0 <= objOther.y0){
        obj.stepX = -obj.step1*Math.cos(a) + obj.step2*Math.sin(a)
        obj.stepY = -obj.step1*Math.sin(a) - obj.step2*Math.cos(a)
    }
    //左下
    if(obj.x0 < objOther.x0 && obj.y0 > objOther.y0){
        obj.stepX = -obj.step1*Math.cos(a) + obj.step2*Math.sin(a)
        obj.stepY = obj.step1*Math.sin(a) + obj.step2*Math.cos(a) 
    }
    //右上
    if(obj.x0 > objOther.x0 && obj.y0 < objOther.y0){
        obj.stepX = obj.step1*Math.cos(a) - obj.step2*Math.sin(a)
        obj.stepY = -obj.step1*Math.sin(a) - obj.step2*Math.cos(a)
    }
    //右下
    if(obj.x0 > objOther.x0 && obj.y0 > objOther.y0){
        obj.stepX = obj.step1*Math.cos(a) - obj.step2*Math.sin(a)
        obj.stepY = obj.step1*Math.sin(a) + obj.step2*Math.cos(a)
    }
}
</script>      
</body>
</html>

有損碰撞

  勻速有損碰撞是在無損碰撞的基礎上,每次碰撞都有一定的速度損耗,在碰撞或碰壁的瞬間乘以損耗因子即可

  變速有損碰撞類似于擊打臺球,元素運動時,速度就一直在減小,碰撞或碰壁時,除了速度方向改變外,速度也有所損耗,當速度減小到0時,停止運動。由于代碼相似,就不再重復,源碼見此


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()