目的:通過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>
歡迎交流學習!!!
不定時隨緣更新。
