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>
欢迎交流学习!!!
不定时随缘更新。