js小球重力和碰撞效果

你猜我叫啥發表於2018-05-29

最近在玩小程式中一個比較火的遊戲,最!強!彈!一!彈!!!,真的是超級模型的一款小遊戲,打發時間堪比抖音。。。。。。,今天就來研究下關於小球如何有重力效果互相碰撞反彈的效果。

1. 重力效果實現

  • 首先我們當然是要先畫個球球啦!
.ball {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background: #ff5532;
    position: absolute;
}
複製程式碼
  • 然後我們需要給小球規定一個初始的X,Y上的速度和一個讓小球運動起來的定時器timer
var speedX = 6;
var speedY = 0;
var timer = null;
複製程式碼
  • 定義小球在X,Y上可以移動的最大距離。
//document.documentElement.clientHeight(螢幕高度)
//ball.offsetHeight(小球的高度)
var maxMoveH = document.documentElement.clientHeight - ball.offsetHeight;
var maxMoveW = document.documentElement.clientWidth - ball.offsetWidth;
複製程式碼
  • 如果達到最小或最大距離,折返運動。
//y軸橫向移動超過做大可移動高度,就反彈折回。 
if(ballMoveY >= maxMoveH || ballMoveY <= 0){
    speedY = -speedY;
    ballMoveY = maxMoveH;
}
//x軸橫向移動超過做大可移動寬度,就反彈折回。
if(ballMoveX >= maxMoveW){
    speedX = -speedX;
    //定死最大寬度
    ballMoveX = maxMoveW;
    speedX = speedX*0.95;
}
//移動到右邊邊界就自動折返。
if(ballMoveX <= 0){
    speedX = -speedX;
    speedX = speedX*0.95;
}
複製程式碼
  • 為了防止小球不在彈起後,還在x軸上移動。
if(Math.abs(speedX) < 0.2 && ballMoveY >= maxMoveH){
    clearInterval(timer);
}
複製程式碼
  • 下面是完整程式碼。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style type="text/css">
        .ball {
            width: 100px;
            height: 100px;
            border-radius: 50%;
            background: #ff6632;
            position: absolute;
        }
    </style>
</head>
<body>
    <button id="moveStart">點選觸發</button>
    <div id="ball" class="ball"></div>
</body>
<script>
    window.onload = function() {
        var ball = document.getElementById("ball");
        var moveStart = document.getElementById("moveStart");
        moveStart.onclick = function() {
            var speedX = 6;
            var speedY = 0;
            var timer = null;
            moveStart();
            function moveStart() {
                clearInterval(timer);
                timer = setInterval(function(){ 
                    var ballMoveY, ballMoveX;
                    var maxMoveH = document.documentElement.clientHeight - ball.offsetHeight;
                    var maxMoveW = document.documentElement.clientWidth - ball.offsetWidth;
                    speedY += 6;
                    //橫向移動會有能量損耗
                    speedX = speedX*0.99;

                    ballMoveY = ball.offsetTop + speedY;
                    ballMoveX = ball.offsetLeft + speedX;

                    if(ballMoveY >= maxMoveH || ballMoveY <= 0){
                        speedY = -speedY;
                        ballMoveY = maxMoveH;
                    }

                    //x軸橫向移動超過做大可移動寬度,就反彈折回
                    if(ballMoveX >= maxMoveW){
                        speedX = -speedX;
                        ballMoveX = maxMoveW;
                        speedX = speedX*0.95;
                    }


                    if(ballMoveX <= 0){
                        speedX = -speedX;
                        speedX = speedX*0.95;
                    }

                    //如果速度小於0.2且小球不在彈起,則不讓小球在移動了
                    if(Math.abs(speedX) < 0.2 && ballMoveY >= maxMoveH){
                        clearInterval(timer);
                    }

                    ball.style.top = ballMoveY + 'px';
                    ball.style.left = ballMoveX + 'px';
                },30) 
            }
        }
    }
</script>
</html>
複製程式碼

上述大概就是簡單的一個js仿重力的簡單實現。

現在我們來看一看神奇的碰撞效果。

首先,我們需要知道實現小球碰撞需要哪些方法。

  1. 隨機生成方法。
function random(type) {
    switch(type){
        case 'color': 
            return 'rgb(' + Math.floor(Math.random()*255) + ',' +  Math.floor(Math.random()*255) + ',' +  Math.floor(Math.random()*255) + ')';
        case 'top':
            return Math.floor(Math.random()*280) + 'px';
        case 'left':
            return Math.floor(Math.random()*280) + 'px';
        case 'speed':
            return Math.floor((Math.random() - 0.5)*20);
        default: 
            return false;
    }
}
複製程式碼
  1. 定義一個碰撞區域。
 #content {
    width: 300px;
    height: 300px;
    border: 1px solid #f00;
    position: relative;
}
複製程式碼
  1. 建立小球。
function createBall(num){
    var p = null;
    for(var i=0; i < num; i++){
        p = document.createElement('p');
        p.innerHTML = i + 1;
        p.style.background = random('color');
        p.style.color = '#fff';
        p.style.top = random('top');
        p.style.left = random('left');
        p.style['textAlign'] = 'center';
        p.id = 'ball' + i;
        content.appendChild(p);
        ballList.push(p);
        moveBall(p);
    }
}
複製程式碼
  1. 小球移動。
function moveBall(ball, startSX, startSY) {
    var speedX = random('speed'),
        speedY = random('speed'),
        ballMoveX = null,
        speedObj = {},
        ballMoveY = null;
    timer = setInterval(function(){
        ballMoveY = ball.offsetTop + speedY;
        ballMoveX = ball.offsetLeft + speedX;
        if(ballMoveX >= maxMoveX || ballMoveX <= 0){
            speedX = -speedX;
        }
        if(ballMoveY >= maxMoveY || ballMoveY <= 0){
            speedY = -speedY;
        }
        ball.style.top = ballMoveY + 'px';
        ball.style.left = ballMoveX + 'px';

        speedObj = crashBall(ball, speedX, speedY);
        speedX = speedObj.speedX;
        speedY = speedObj.speedY;
    }, 30)
}
複製程式碼
  1. 小球碰撞。

存在8種位置碰撞的情況。

js小球重力和碰撞效果

function crashBall(ball, speedX, speedY) {
    var speedObj = {};
    for(var i = 0; i<ballList.length; i++){
        //自己不跟自己比較
        if(ball.id !== ballList[i].id && Math.abs(ball.offsetLeft - ballList[i].offsetLeft) <= 20 && Math.abs(ball.offsetTop - ballList[i].offsetTop) <= 20){
            //**********物件小球在碰撞小球方位的8個情況***********
            //左上方
            if(ball.offsetLeft < ballList[i].offsetLeft && ball.offsetTop < ballList[i].offsetTop){
                speedX > 0 && (speedX = -speedX);
                speedY > 0 && (speedY = -speedY);
            }
            //正上方(不會影響水平運動的速度)
            if(ball.offsetLeft === ballList[i].offsetLeft && ball.offsetTop < ballList[i].offsetTop){
                speedY > 0 && (speedY = -speedY);
            }
            //右上方
            if(ball.offsetLeft > ballList[i].offsetLeft && ball.offsetTop < ballList[i].offsetTop){
                speedX < 0 && (speedX = -speedX);
                speedY > 0 && (speedY = -speedY);
            }
            //正右方
            if(ball.offsetLeft > ballList[i].offsetLeft && ball.offsetTop === ballList[i].offsetTop){
                speedX < 0 && (speedX = -speedX);
            }
            //右下方
            if(ball.offsetLeft > ballList[i].offsetLeft && ball.offsetTop > ballList[i].offsetTop){
                speedX < 0 && (speedX = -speedX);
                speedY < 0 && (speedY = -speedY);
            }
            //正下方
            if(ball.offsetLeft === ballList[i].offsetLeft && ball.offsetTop > ballList[i].offsetTop){
                speedY < 0 && (speedY = -speedY);
            }
            //左下方
            if(ball.offsetLeft < ballList[i].offsetLeft && ball.offsetTop > ballList[i].offsetTop){
                speedX > 0 && (speedX = -speedX);
                speedY < 0 && (speedY = -speedY);
            }
            //正左方
            if(ball.offsetLeft < ballList[i].offsetLeft && ball.offsetTop === ballList[i].offsetTop){
                speedX > 0 && (speedX = -speedX);
            }
        }
    }
    speedObj.speedX = speedX;
    speedObj.speedY = speedY;
    return speedObj;
}
複製程式碼

完整程式碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style type="text/css">
        html,body {
            margin: 0;
            padding: 0;
        }

        #content {
            width: 300px;
            height: 300px;
            border: 1px solid #f00;
            position: relative;
        }

        #content p {
            position: absolute;
            width: 20px;
            height: 20px;
            border-radius: 50%;
            margin: 0;
        }

    </style>
</head>
<body>
    <div id="content"></div>
</body>
<script>
    window.onload = function() {
        var content = document.getElementById('content');
        var maxMoveX = content.clientWidth - 20;
        var maxMoveY = content.clientHeight - 20;
        var timer = null;
        var ballList = [];
        //0. 公共方法(隨機建立)
        function random(type) {
            switch(type){
                case 'color': 
                    return 'rgb(' + Math.floor(Math.random()*255) + ',' +  Math.floor(Math.random()*255) + ',' +  Math.floor(Math.random()*255) + ')';
                case 'top':
                    return Math.floor(Math.random()*280) + 'px';
                case 'left':
                    return Math.floor(Math.random()*280) + 'px';
                case 'speed':
                    return Math.floor((Math.random() - 0.5)*20);
                default: 
                    return false;
            }
        }
        //1. 建立小球
        function createBall(num){
            var p = null;
            for(var i=0; i < num; i++){
                p = document.createElement('p');
                p.innerHTML = i + 1;
                p.style.background = random('color');
                p.style.color = '#fff';
                p.style.top = random('top');
                p.style.left = random('left');
                p.style['textAlign'] = 'center';
                p.id = 'ball' + i;
                content.appendChild(p);
                ballList.push(p);
                moveBall(p);
            }
        }
        //2. 移動小球
        function moveBall(ball, startSX, startSY) {
            var speedX = random('speed'),
                speedY = random('speed'),
                ballMoveX = null,
                speedObj = {},
                ballMoveY = null;
            timer = setInterval(function(){
                ballMoveY = ball.offsetTop + speedY;
                ballMoveX = ball.offsetLeft + speedX;
                if(ballMoveX >= maxMoveX || ballMoveX <= 0){
                    speedX = -speedX;
                }
                if(ballMoveY >= maxMoveY || ballMoveY <= 0){
                    speedY = -speedY;
                }
                ball.style.top = ballMoveY + 'px';
                ball.style.left = ballMoveX + 'px';

                speedObj = crashBall(ball, speedX, speedY);
                speedX = speedObj.speedX;
                speedY = speedObj.speedY;
            }, 30)
        }
        //3. 處理小球間的碰撞
        function crashBall(ball, speedX, speedY) {
            var speedObj = {};
            for(var i = 0; i<ballList.length; i++){
                //自己不跟自己比較
                if(ball.id !== ballList[i].id && Math.abs(ball.offsetLeft - ballList[i].offsetLeft) <= 20 && Math.abs(ball.offsetTop - ballList[i].offsetTop) <= 20){
                    //**********物件小球在碰撞小球方位的8個情況***********
                    //左上方
                    if(ball.offsetLeft < ballList[i].offsetLeft && ball.offsetTop < ballList[i].offsetTop){
                        speedX > 0 && (speedX = -speedX);
                        speedY > 0 && (speedY = -speedY);
                    }
                    //正上方(不會影響水平運動的速度)
                    if(ball.offsetLeft === ballList[i].offsetLeft && ball.offsetTop < ballList[i].offsetTop){
                        speedY > 0 && (speedY = -speedY);
                    }
                    //右上方
                    if(ball.offsetLeft > ballList[i].offsetLeft && ball.offsetTop < ballList[i].offsetTop){
                        speedX < 0 && (speedX = -speedX);
                        speedY > 0 && (speedY = -speedY);
                    }
                    //正右方
                    if(ball.offsetLeft > ballList[i].offsetLeft && ball.offsetTop === ballList[i].offsetTop){
                        speedX < 0 && (speedX = -speedX);
                    }
                    //右下方
                    if(ball.offsetLeft > ballList[i].offsetLeft && ball.offsetTop > ballList[i].offsetTop){
                        speedX < 0 && (speedX = -speedX);
                        speedY < 0 && (speedY = -speedY);
                    }
                    //正下方
                    if(ball.offsetLeft === ballList[i].offsetLeft && ball.offsetTop > ballList[i].offsetTop){
                        speedY < 0 && (speedY = -speedY);
                    }
                    //左下方
                    if(ball.offsetLeft < ballList[i].offsetLeft && ball.offsetTop > ballList[i].offsetTop){
                        speedX > 0 && (speedX = -speedX);
                        speedY < 0 && (speedY = -speedY);
                    }
                    //正左方
                    if(ball.offsetLeft < ballList[i].offsetLeft && ball.offsetTop === ballList[i].offsetTop){
                        speedX > 0 && (speedX = -speedX);
                    }
                }
            }
            speedObj.speedX = speedX;
            speedObj.speedY = speedY;
            return speedObj;
        }
        createBall(3);
    }
</script>
</html>
複製程式碼

關鍵的地方是要向清楚,兩個小球間碰撞後的運動方向,和碰到方框以後的碰撞方向。

好啦,上面就是今天的全部內容啦。

end。。。

相關文章