javascript運動系列第八篇——碰壁運動


前面的話

  碰撞運動可能是運動系列里面比較復雜的運動了。碰撞可以分為碰壁和互碰兩種形式,而碰撞前后的運動形式也可以分為變速和勻速兩種,如果再涉及到阻力,具有速度損耗的話,就更加復雜了。本文先介紹碰壁運動

 

勻速碰壁

  碰壁是一種常見的碰撞形式,勻速碰壁是最簡單的碰撞運動

  假設一個密閉空間內一個彈性小球,小球有一個隨機方向隨機大小的初始速度。當小球碰壁時,速度不損失,而是做反向的勻速運動

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div id="test" style="height: 100px;width: 100px;background:lightblue;position:absolute;top:60px;left:20px;border-radius:50%;"></div>

<button id="btn1">開始運動</button>
<button id="btn2">停止運動</button>
<span>游戲說明:當小球開始運動后,點擊小球一次得一分</span>
<div id="result"></div>
<script>
var timer,i=0;
//聲明得分
var key = 0;
var arr = ['orange','lightgreen','lightcoyal','pink','lightcyan','lightgray','lightseagreen','lightsteelblue'];

function changeColor(){
    i++;
    if(i == arr.length){
        i = 0;
    }
    test.style.background = arr[i];    
}
document.onmousemove = function(){
    return false;
}
test.onclick = function(){
    //當小球開始運動后,開始記分
    if(test.timer){
        result.innerHTML = '當前得分為:' + ++key + ''
    }
    changeColor();
}
btn1.onclick = function(){
    result.innerHTML = ''
    //將分數清零
    key = 0;
    collisionMove({
        obj:test
    })
    clearInterval(timer);
    timer = setInterval(function(){
        changeColor();
    },500);
}
btn2.onclick = function(){
    clearInterval(timer);
    clearInterval(test.timer);
    test.timer = 0;
    result.innerHTML = '你得到:' + key + '分,再接再厲!'
}
function getCSS(obj,style){
    if(window.getComputedStyle){
        return getComputedStyle(obj)[style];
    }
    return obj.currentStyle[style];
}  
function collisionMove(json){
    var obj = json.obj;
    var fn = json.fn;
    //聲明x、y軸的當前值
    var curX = parseFloat(getCSS(obj,'left'));
    var curY = parseFloat(getCSS(obj,'top'));
    //聲明x、y軸的步長值
    var stepX = json.stepX;
    var stepY = json.stepY;
    //步長值默認值為[-25,-20,-15,-10,-5,0,5,10,15,20]中的一個隨機數
    stepX = Number(stepX) || 5*Math.floor(Math.random() * 10 - 5);
    stepY = Number(stepY) || 5*Math.floor(Math.random() * 10 - 5);
    //聲明x、y軸方向
    var dirX = json.dirX;
    var dirY = json.dirY;
    dirX = stepX > 0 ? '+' : '-';
    dirY = 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;
    var top;
    //清除定時器
    if(obj.timer){return;}
     //開啟定時器
    obj.timer = setInterval(function(){
        //獲取x、y軸的當前值
        curX = parseFloat(getCSS(obj,'left'));
        curY = parseFloat(getCSS(obj,'top'));
        //更新left、top值
        left = curX + stepX;
        top = curY + stepY;
        //右側碰壁前一刻,步長大於剩余距離,且元素向右運動時
        if((left > activeWidth - offsetWidth) && (dirX == '+')){
            left = activeWidth - offsetWidth;
        }
        //左側碰壁前一刻,步長大於剩余距離,且元素向左運動時
        if((Math.abs(stepX) > curX) && (dirX == '-')){
            left = curX;
        }
        //下側碰壁前一刻,步長大於剩余距離,且元素向下運動時
        if((top > activeHeight - offsetHeight) && (dirY == '+')){
            top = activeHeight - offsetHeight;
        }
        //上側碰壁前一刻,步長大於剩余距離,且元素向上運動時
        if((Math.abs(stepY) > curY) && (dirY == '-')){
            top = curY;
        }
        obj.style.left= left + 'px';
        obj.style.top = top + 'px';
        //左側或右側碰撞瞬間
        if(left == activeWidth - offsetWidth || left == curX){
            stepX = -stepX;
        }
        //上側或下側碰撞瞬間
        if(top == activeHeight - offsetHeight || top == curY){
            stepY = -stepY;
        }
        //更新運動方向
        dirX = stepX > 0 ? '+' : '-';
        dirY = stepY > 0 ? '+' : '-';
    },20);            
}
</script>    
</body>
</html>

自由落體

  元素在實際運動中,並不是保持勻速運動的,更多的是變速運動,而且會有速度損耗。典型的場景是自由落體運動,物體落地之后會反方向彈幾下,最終停在地上

  自由落體運動可以看做是重力與阻力合作的結果。在空中運動時,向下運動時,做勻加速運動;向上運動時,做勻減速運動。與地面碰撞的瞬間,產生速度損耗

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div id="test" style="height:50px;width: 50px;background:lightblue;position:absolute;top:60px;border-radius:50%;"></div>
<button id="btn1">開始運動</button>
<button id="btn2">還原</button>
<script>
document.onmousedown = function(){
    return false;
}
btn1.onclick = function(){
    collisionMove({
        obj:test
    })
}
btn2.onclick = function(){
    history.go();
}
function getCSS(obj,style){
    if(window.getComputedStyle){
        return getComputedStyle(obj)[style];
    }
    return obj.currentStyle[style];
}  
function collisionMove(json){
    var obj = json.obj;
    var fn = json.fn;
    //聲明y軸的當前值
    var curY = parseFloat(getCSS(obj,'top'));
    //聲明offset高
    var offsetHeight = obj.offsetHeight;
    //聲明元素活動區域高
    var activeHeight = json.activeHeight;
    //元素獲取區域寬高默認值為可視區域高
    activeHeight = Number(activeHeight) || document.documentElement.clientHeight;
    //聲明y軸的步長值
    var stepY = 0;
    //聲明top樣式值
    var top;
    //聲明減速系數
    var k = 0.8;
    //聲明碰撞次數
    var i = 0;
    //清除定時器
    if(obj.timer){return;}
     //開啟定時器
    obj.timer = setInterval(function(){
        //獲取y軸的當前值
        curY = parseFloat(getCSS(obj,'top'));
        //更新步長值stepY
        stepY+= 5;
        //更新top值
        top = curY + stepY;
        //下側碰壁前一刻,步長大於剩余距離,且元素向下運動時
        if(top > activeHeight - offsetHeight){
            top = activeHeight - offsetHeight;
        }
        obj.style.top = top + 'px';
        //下側碰撞瞬間,改變運動方向,並產生速度損耗
        if(top == activeHeight - offsetHeight){
            //若碰撞10次后,則停止運動
            i++;
            if(i== 10){
                clearInterval(obj.timer)
                obj.timer = 0;
                fn && fn.call(obj);
            }
            stepY = -stepY * k;
        }
    },20);            
}
</script>    
</body>
</html>

投擲碰壁

  如果一個物體向空中投擲出去,會呈現一個拋物線的效果,最終經過與地面碰撞多次后停止

  投擲碰撞效果是x軸和y軸的合效果。x軸做勻速運動,當物體碰到地面后,x軸速度發生損耗;y軸做勻加速運動,當物體碰到地面后,y軸速度同樣發生損耗

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div id="test" style="height: 50px;width: 50px;background:lightblue;position:absolute;top:200px;left:20px;border-radius:50%;"></div>
<button id="btn1">開始運動</button>
<button id="btn2">還原</button>
<script>
document.onmousedown = function(){
    return false;
}
btn1.onclick = function(){
    collisionMove({
        obj:test
    })
}
btn2.onclick = function(){
    history.go();
}
function getCSS(obj,style){
    if(window.getComputedStyle){
        return getComputedStyle(obj)[style];
    }
    return obj.currentStyle[style];
}  
function collisionMove(json){
    var obj = json.obj;
    var fn = json.fn;
    //聲明y軸的當前值
    var curY = parseFloat(getCSS(obj,'top'));
    //聲明offset高
    var offsetHeight = obj.offsetHeight;
    //聲明元素活動區域高
    var activeHeight = json.activeHeight;
    //元素獲取區域寬高默認值為可視區域高
    activeHeight = Number(activeHeight) || document.documentElement.clientHeight;
    //聲明x、y軸的步長值
    var stepY = -50;
    var stepX = 10;
    //聲明top、left樣式值
    var top;
    var left;
    //聲明減速系數
    var k = 0.8;
    //聲明碰撞次數
    var i = 0;
    //清除定時器
    if(obj.timer){return;}
     //開啟定時器
    obj.timer = setInterval(function(){
        //獲取x、y軸的當前值
        curX = parseFloat(getCSS(obj,'left'));
        curY = parseFloat(getCSS(obj,'top'));
        //更新步長值stepY
        stepY+= 5;
        //更新top、left值
        top = curY + stepY;
        left = curX + stepX;
        //下側碰壁前一刻,步長大於剩余距離,且元素向下運動時
        if(top > activeHeight - offsetHeight){
            top = activeHeight - offsetHeight;
        }
        obj.style.top = top + 'px';
        obj.style.left = left + 'px';
        //下側碰撞瞬間,改變運動方向,並產生速度損耗
        if(top == activeHeight - offsetHeight){
            //若碰撞10次后,則停止運動
            i++;
            if(i== 10){
                clearInterval(obj.timer)
                obj.timer = 0;
                fn && fn.call(obj);
            }
            //速度損耗
            stepY = -stepY * k;
            stepX = stepX * k;
        }
    },20);            
}
</script>    
</body>
</html>

拖拽碰壁

  實際情況下,一個物體默認具有重力效果。物體的重力效果是時時刻刻都在發生的,相當於定時器的每次運動,都有向下的勻加速運動

    如果投擲速度不同,則運動速度也不相同。在碰壁的情況下,速度會有損耗,並且發生速度方向變化。最終,物體會落到地上

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div id="test" style="height: 100px;width: 100px;border-radius:50%;background:pink;position:absolute;top:40px;left:0;"></div>
<script>
//聲明元素投擲步長值
var stepX=0,stepY=0;
//默認情況下,也存在重力效果
collisionMove({
    obj:test,
    stepX:stepX,
    stepY:stepY
})
function getCSS(obj,style){
    if(window.getComputedStyle){
        return getComputedStyle(obj)[style];
    }
    return obj.currentStyle[style];
} 
//碰撞運動函數
function collisionMove(json){
    var obj = json.obj;
    var fn = json.fn;
    //聲明x、y軸的當前值
    var curX = parseFloat(getCSS(obj,'left'));
    var curY = parseFloat(getCSS(obj,'top'));
    //聲明x、y軸的步長值
    var stepX = json.stepX;
    var stepY = json.stepY;
       //聲明元素的重要加速度
       var g = json.g || 3;
    //步長值默認值為10
    if(isNaN(Number(stepX))){
        stepX = 10;
    }else{
        stepX = Number(stepX);
    }
    if(isNaN(Number(stepY))){
        stepY = 10;
    }else{
        stepY = Number(stepY);
    }
    //聲明x、y軸方向
    var dirX = json.dirX;
    var dirY = json.dirY;
    dirX = stepX > 0 ? '+' : '-';
    dirY = 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;
    var top;
    //聲明減速系數
    var k = 0.8;
    //聲明碰撞次數
    var i = 0;
    //清除定時器
    if(obj.timer){return;}
     //開啟定時器
    obj.timer = setInterval(function(){
        //獲取x、y軸的當前值
        curX = parseFloat(getCSS(obj,'left'));
        curY = parseFloat(getCSS(obj,'top'));
        //受到重力影響,更新步長值stepY
        stepY += g;
        //更新left、top值
        left = curX + stepX;
        top = curY + stepY;
        //右側碰壁前一刻,步長大於剩余距離,且元素向右運動時
        if((left > activeWidth - offsetWidth) && (dirX == '+')){
            left = activeWidth - offsetWidth;
        }
        //左側碰壁前一刻,步長大於剩余距離,且元素向左運動時
        if((Math.abs(stepX) > curX) && (dirX == '-')){
            left = curX;
        }
        //下側碰壁前一刻,步長大於剩余距離,且元素向下運動時
        if((top > activeHeight - offsetHeight) && (dirY == '+')){
            top = activeHeight - offsetHeight;
        }
        //上側碰壁前一刻,步長大於剩余距離,且元素向上運動時
        if((Math.abs(stepY) > curY) && (dirY == '-')){
            top = curY;
        }
        obj.style.left= left + 'px';
        obj.style.top = top + 'px';
        //左側或右側碰撞瞬間
        if(left == activeWidth - offsetWidth || left == curX){
            //x軸方向速度損耗,並發生方向變化
            stepX = -stepX * k;
        }
        //上側或下側碰撞瞬間
        if(top == activeHeight - offsetHeight || top == curY){
            //y軸方向速度損耗,並發生方向變化
            stepY = -stepY * k;
            //x軸方向速度損耗
            stepX = stepX * k;
        }
        //元素與地面碰撞10次后,則停止運動
        if(top == activeHeight - offsetHeight){
            i++;
            if(i== 10){
                clearInterval(obj.timer)
                obj.timer = 0;
                fn && fn.call(obj);
            }            
        }
        //更新運動方向
        dirX = stepX > 0 ? '+' : '-';
        dirY = stepY > 0 ? '+' : '-';
    },20);            
}    
//初始拋擲
test.onmousedown = function(e){
    e = e || event;
    //聲明上一次mousemove事件的坐標位置
    var lastX2 = e.clientX;
    var lastY2 = e.clientY;
    //獲取元素距離定位父級的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 = this.offsetHeight;
    var EW = this.offsetWidth;
    document.onmousemove = function(e){
        e = e || event;
        //獲取此時鼠標距離視口左上角的x軸及y軸距離
        var x2 = e.clientX;
        var y2 = e.clientY; 
        stepX = x2 - lastX2;
        stepY = y2 - lastY2;  
        lastX2 = e.clientX;
        lastY2 = 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,使元素移動到相應位置
        test.style.left = X + 'px';
        test.style.top = Y + 'px';
    }
    document.onmouseup = function(e){
        e = e || event;
        var maxHeight = document.documentElement.clientHeight - test.offsetHeight;
        var maxWidth = document.documentElement.clientWidth - test.offsetWidth;
        //以設置的投擲速度來進行碰撞運動
        collisionMove({
            obj:test,
            stepX:stepX,
            stepY:stepY
        })
        //當鼠標抬起時,拖拽結束,則將onmousemove賦值為null即可
        document.onmousemove = null;
        //釋放全局捕獲
        if(test.releaseCapture){
            test.releaseCapture();
        }
    }
    //阻止默認行為
    return false;
    //IE8-瀏覽器阻止默認行為
    if(test.setCapture){
        test.setCapture();
    }
}
</script>
</body>
</html>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM