Canvas+Js制作動量守恆的小球碰撞


目的:通過js實現小球碰撞並實現動量守恆

canvas我們就不多說了,有用着呢。

我們可以通過canvas畫2D圖形(圓、方塊、三角形等等)3D圖形(球體、正方體等待)。

當然這只是基礎的皮毛而已,canvas的強大之處在於可以做游戲,導入模型,粒子效果,實現漫游又或者全景和VR。

這里我們介紹純js寫的2D小球碰撞。(主要是博主的Three.js不咋地)

好吧,老規矩,先上圖!

額。。。很尷尬的是博主的截圖功底不咋地,沒有截下碰撞的瞬間。

話不多說,開始教程。

首先我們需要創建畫布給它一個id方便后面監聽處理。

<body>
<canvas id="myCanvas"></canvas>
</body>

然后是Js代碼

//聲明畫布大小為屏幕的1/3
    var width = window.innerWidth/3;
    var  height = window.innerHeight/3;
    var canvas = document.getElementById("myCanvas");
    canvas.width = width;
    canvas.height = height;
    //創建2d畫筆
    var ctx = canvas.getContext("2d");
    //填充顏色設置為黑色(背景色)
    ctx.fillStyle = "#000";
    //將整個畫布填充
    ctx.fillRect(0,0,width,height);

這是Canvas最基本的操作,我們解釋一下fillRect(x,y,width,height);這個函數。

x和y填充的起始坐標點,width和height是填充區域的寬和高。

 由於我們創建的是帶有物理性質的小球,所以我們用一個函數封裝創建小球的代碼。

以后創建小球直接調用它就行了。

function Ball(x,y,vx,vy,ax,ay,size,rou,color,ctx){
    //參數傳值
    //x,y為坐標點 vx,vy為小球水平和垂直方向上的速度 ax,ay為加速度 
    //size 為大小 rou為密度 color顏色 ctx畫筆
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
    this.rou = rou;
    this.size = size;
    this.ax = ax;
    this.ay = ay;
    this.m = Math.PI*this.size*this.size*rou;//求出質量
    
    this.draw = function(ctx){
        ctx.fillStyle=color;
        //console.log(this.x, this.y,this.size);
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.size, 0, Math.PI*2,false);//畫圓
        ctx.fill();
        ctx.closePath();
    }
    this.draw(ctx);
}

接下來實例化出兩個小球。

//碰撞檢測 動量守恆
    //x,y,vx,vy,ax,ay,size,rou,color,ctx
    var balla = new Ball(20,0.5*height,5,-3,0,0,8,1,"#ff0",ctx);
    var ballb = new Ball(width-20,0.5*height,-3,5,0,0,13,1,"#0ff",ctx);
    var ballc = new Ball(width/2,0.5*height,7,4,0,0,13,1,"#0ff",ctx);

然后我們封裝了一個函數來實現小球是實時更新。

function animation(){

        //小球的速度等於速度加上加速度
        balla.vx+= balla.ax;
        balla.vy+=balla.ay;
        //小球的位移等於小球現在的坐標加上速度
        balla.x+= balla.vx;
        balla.y+=balla.vy;

        ballb.vx+= ballb.ax;
        ballb.vy+=ballb.ay;
        ballb.x+= ballb.vx;
        ballb.y+=ballb.vy;
        //基於距離的碰撞檢測
        var pointdis=(balla.x-ballb.x)*(balla.x-ballb.x)+(balla.y-ballb.y)*(balla.y-ballb.y);//坐標距離
        var pointsize=(balla.size+ballb.size)*(balla.size+ballb.size);//半徑距離
        if( pointdis <= pointsize)
        {
            console.log("haha");
            //這里是能量守恆公式
            var ballavx =((balla.m-ballb.m)*balla.vx+2*ballb.m*ballb.vx)/(balla.m+ballb.m);
            var ballavy =((balla.m-ballb.m)*balla.vy+2*ballb.m*ballb.vy)/(balla.m+ballb.m);
            var ballbvx=((ballb.m-balla.m)*ballb.vx+2*balla.m*balla.vx)/(balla.m+ballb.m);
            var ballbvy=((ballb.m-balla.m)*ballb.vy+2*balla.m*balla.vy)/(balla.m+ballb.m);
            balla.vx = ballavx;
            balla.vy = ballavy;

            ballb.vx = ballbvx;
            ballb.vy = ballbvy;
            //小Bug改進
            if(Math.abs(balla.vx-ballb.vx)<0.01&&Math.abs(balla.vy-ballb.vy)<0.01)
            {
                console.log(balla.vx);
                balla.vx=-balla.vx;
                balla.vy=-balla.vy;
                return;
            }
        }
        
        //判斷是否碰撞到畫布的邊緣
        if(balla.x+balla.size>=width||balla.x-balla.size<=0)
        {
            balla.vx*=-0.98;
        }
        if(balla.y+balla.size>=height||balla.y-balla.size<=0)
        {
            balla.vy*=-0.98;
        }

        if(ballb.x+ballb.size>=width||ballb.x-ballb.size<=0)
        {
            ballb.vx*=-0.98;
        }
        if(ballb.y+ballb.size>=height||ballb.y-ballb.size<=0)
        {
            ballb.vy*=-0.98;
        }
        
        //清空畫布,畫出小球
        ctx.fillStyle = "#000";
        ctx.fillRect(0,0,width,height);
        balla.draw(ctx);
        ballb.draw(ctx);
        //console.log(ballb.vy);

    }

最后我們讓他30毫秒更新一次。

    setInterval(animation,30);

OK,又大功告成了。自己動手試試吧!

懶人福利!!!

完整代碼。

<html>
<head>
<style>
body{margin:0;}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
<script>
    //聲明畫布大小為屏幕的1/3
    var width = window.innerWidth/3;
    var  height = window.innerHeight/3;
    var canvas = document.getElementById("myCanvas");
    canvas.width = width;
    canvas.height = height;
    //創建2d畫筆
    var ctx = canvas.getContext("2d");
    //填充顏色設置為黑色(背景色)
    ctx.fillStyle = "#000";
    //將整個畫布填充
    ctx.fillRect(0,0,width,height);
    
    //碰撞檢測 動量守恆
    //x,y,vx,vy,ax,ay,size,rou,color,ctx
    var balla = new Ball(20,0.5*height,5,-3,0,0,8,1,"#ff0",ctx);
    var ballb = new Ball(width-20,0.5*height,-3,5,0,0,13,1,"#0ff",ctx);
    var ballc = new Ball(width/2,0.5*height,7,4,0,0,13,1,"#0ff",ctx);
    
    setInterval(animation,30);
    function animation(){

        //小球的速度等於速度加上加速度
        balla.vx+= balla.ax;
        balla.vy+=balla.ay;
        //小球的位移等於小球現在的坐標加上速度
        balla.x+= balla.vx;
        balla.y+=balla.vy;

        ballb.vx+= ballb.ax;
        ballb.vy+=ballb.ay;
        ballb.x+= ballb.vx;
        ballb.y+=ballb.vy;
        //基於距離的碰撞檢測
        var pointdis=(balla.x-ballb.x)*(balla.x-ballb.x)+(balla.y-ballb.y)*(balla.y-ballb.y);//坐標距離
        var pointsize=(balla.size+ballb.size)*(balla.size+ballb.size);//半徑距離
        if( pointdis <= pointsize)
        {
            console.log("haha");
            //這里是能量守恆公式
            var ballavx =((balla.m-ballb.m)*balla.vx+2*ballb.m*ballb.vx)/(balla.m+ballb.m);
            var ballavy =((balla.m-ballb.m)*balla.vy+2*ballb.m*ballb.vy)/(balla.m+ballb.m);
            var ballbvx=((ballb.m-balla.m)*ballb.vx+2*balla.m*balla.vx)/(balla.m+ballb.m);
            var ballbvy=((ballb.m-balla.m)*ballb.vy+2*balla.m*balla.vy)/(balla.m+ballb.m);
            balla.vx = ballavx;
            balla.vy = ballavy;

            ballb.vx = ballbvx;
            ballb.vy = ballbvy;
            //小Bug改進
            if(Math.abs(balla.vx-ballb.vx)<0.01&&Math.abs(balla.vy-ballb.vy)<0.01)
            {
                console.log(balla.vx);
                balla.vx=-balla.vx;
                balla.vy=-balla.vy;
                return;
            }
        }
        
        //判斷是否碰撞到畫布的邊緣
        if(balla.x+balla.size>=width||balla.x-balla.size<=0)
        {
            balla.vx*=-0.98;
        }
        if(balla.y+balla.size>=height||balla.y-balla.size<=0)
        {
            balla.vy*=-0.98;
        }

        if(ballb.x+ballb.size>=width||ballb.x-ballb.size<=0)
        {
            ballb.vx*=-0.98;
        }
        if(ballb.y+ballb.size>=height||ballb.y-ballb.size<=0)
        {
            ballb.vy*=-0.98;
        }
        
        //清空畫布,畫出小球
        ctx.fillStyle = "#000";
        ctx.fillRect(0,0,width,height);
        balla.draw(ctx);
        ballb.draw(ctx);
        //console.log(ballb.vy);

    }
function Ball(x,y,vx,vy,ax,ay,size,rou,color,ctx){
    //參數傳值
    //x,y為坐標點 vx,vy為小球水平和垂直方向上的速度 ax,ay為加速度 
    //size 為大小 rou為密度 color顏色 ctx畫筆
    this.x = x;
    this.y = y;
    this.vx = vx;
    this.vy = vy;
    this.rou = rou;
    this.size = size;
    this.ax = ax;
    this.ay = ay;
    this.m = Math.PI*this.size*this.size*rou;//求出質量
    
    this.draw = function(ctx){
        ctx.fillStyle=color;
        //console.log(this.x, this.y,this.size);
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.size, 0, Math.PI*2,false);//畫圓
        ctx.fill();
        ctx.closePath();
    }
    this.draw(ctx);
}
</script>
</html>

歡迎交流學習!!!

不定時隨緣更新。


免責聲明!

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



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