扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
html
创新互联秉承实现全网价值营销的理念,以专业定制企业官网,网站制作、成都网站建设,小程序定制开发,网页设计制作,移动网站建设,成都营销网站建设帮助传统企业实现“互联网+”转型升级专业定制企业官网,公司注重人才、技术和管理,汇聚了一批优秀的互联网技术人才,对客户都以感恩的心态奉献自己的专业和所长。
head
title
大球吃小球by大奔
/title
script type="text/javascript" src="src/jscex.js"/script
script type="text/javascript" src="src/jscex-parser.js"/script
script type="text/javascript" src="src/jscex-jit.js"/script
script type="text/javascript" src="src/jscex-builderbase.js"/script
script type="text/javascript" src="src/jscex-async.js"/script
script type="text/javascript" src="src/jscex-async-powerpack.js"/script
/head
body
canvas id="myCanvas" width="480" height="300" style="border:1px solid #c3c3c3"
你的浏览器改换了
/canvas
script type="text/javascript"
var d=document.getElementByIdx_x("myCanvas");
var cxt=d.getContext("2d");
var balls=[];
//这里为了获得随机数的向量
function getRandom(a,b){
return (a+Math.floor(Math.random()*(b-a+1)))
}
//这里对向量进行赋值
var Vector2=function(a,b){
this.x=a||0;
this.y=b||0;
};
//这里需要注意,对象的默认方法在这里写不会管用。例如sub
Vector2.prototype={//写对象的构造函数
constructor:Vector2,
multiplyScalar:function(s){
this.x*=s;
this.y*=s;
return this;
},
divideScalar:function(s){
if(s){
this.x/=s;
this.y/=s;
}else{
this.set(0,0);
}
return this;
},
dot:function(v){
return this.x*v.x+this.y*v.y;//即两个向量相乘
},
lengthSq:function(){
return this.x*this.x+this.y*this.y;
},
length:function(){
return Math.sqrt(this.lengthSq());
},
normalize:function(){
//这里得到的是单位向量,按照google的定义,单位的向量是, //(a,b)则a*a+b*b=1;
return this.divideScalar(this.length());
},
reflectionSelf:function(v){
//这里得到的是反射向量。公式参考这个网址。
//blog.physwf.com/?p=42
var nv=v.normalize();
this.sub(nv.multiplyScalar(2*this.dot(nv)));
},
distanceToSquared:function(v){//求出两点之间的距离
var dx=this.x-v.x,
dy=this.y-v.y;
return dx*dx+dy*dy;
}
};
Vector2.sub=function(v1,v2){//这里重写sub方法
return new Vector2(v1.x-v2.x,v1.y-v2.y);
};
for(var i=0;i40;i++){//初始化40个小球
var ball={
position:new Vector2(getRandom(20,600),getRandom(20,600)),
r:getRandom(6,20),
speed:new Vector2(getRandom(-200,200),getRandom(-200,200)),
mass:1,//这是小球的质量
restitution:1//这是弹性系数
};
balls.push(ball);
}
var filterBalls=[];
for(var i=0;iballs.length;i++){
var overlapCount=0;
for(var j=i+1;jballs.length;j++){//两个两个比较防止重复,而且初始化的位置不能重 //叠,否则符合碰撞的条件。去掉这个判断以后,效果不太显著,可以多放些球试试。
var distance=balls[i].position.distanceToSquared(balls[j].position);
var l=balls[i].r+balls[j].r;
if(distance=(l*l)){
overlapCount++;
}
}
if(overlapCount===0){
filterBalls.push(balls[i]);
}
}
balls=filterBalls;//这里可以去掉试试。
cxt.fillStyle="#030303";
cxt.fillRect(0,0,d.width,d.height);
function init(){
cxt.fillStyle="#fff";
for(i in balls){
cxt.beginPath();
cxt.arc(balls[i].position.x,balls[i].position.y,balls[i].r,0,Math.PI*2,true);
cxt.closePath();
cxt.fill();
}
}
init();
var cyc=20;
var moveAsync2=eval_r(Jscex.compile("async",function(){
var tag=0;
while(true){
try{
cxt.fillStyle="rgba(0,0,0,3)";
cxt.fillRect(0,0,d.width,d.height);
cxt.fillStyle="#fff";
for(var i=0;iballs.length;i++){
//这里是为了两个小球比较会重复所以,每次比较都是i与i+1 //开始相比较
for(var j=i+1;jballs.length;j++){
collisionSolver(balls[i],balls[j]);
}
}
for(i in balls){
cxt.beginPath();
cxt.arc(balls[i].position.x,balls[i].position.y,balls[i].r,0,Math.PI*2,true);
cxt.closePath();
cxt.fill();
if(balls[i].r+balls[i].position.xd.width){
//如果小球x轴跑出了画布的范围
balls[i].position.x=d.width-balls[i].r;
//小球的位置返回到画布的边缘位置
balls[i].speed.x*=-1;
//同时x轴的方向变为反方向
}if(balls[i].position.xballs[i].r){
//如果小球的x坐标小于小球的半径。肯定画不成完整的圆了,所以要归位
balls[i].position.x=balls[i].r;
balls[i].speed.x*=-1; }if(balls[i].r+balls[i].position.yd.height){//同理y轴
balls[i].position.y=d.height-balls[i].r;
balls[i].speed.y*=-1;
}
if(balls[i].position.yballs[i].r){
balls[i].position.y=balls[i].r;
balls[i].speed.y*=-1;
} balls[i].position.x+=balls[i].speed.x*cyc/1000;
//小球的x轴不断按照速度增大
balls[i].position.y+=balls[i].speed.y*cyc/1000;
}
}catch(e){
alert(e);
}
$await(Jscex.Async.sleep(cyc));
}
}));
function collisionSolver(bodyA,bodyB){//判断小球发生碰撞的时候的变化。
var vB=bodyB.speed;
var vA=bodyA.speed;
var l=bodyA.r+bodyB.r;
var distSqr=bodyA.position.distanceToSquared(bodyB.position);
var isTouching=distSqr=l*l? true:false;
//判断两圆心之间的距离如果小于两半径之和的平方。则为true
var normal=Vector2.sub(bodyB.position,bodyA.position).normalize();
//请看上面的解释,所以得到的是B相对于A的单位向量。
var ratio=bodyA.r/l;//这是一个比例
var contactPoint=new Vector2();
//根据平行线切割的三角形,两边的边的比例相等,
contactPoint.x=bodyA.position.x+(bodyB.position.x-bodyA.position.x)*ratio;
contactPoint.y=bodyA.position.y+(bodyB.position.y-bodyA.position.y)*ratio;
var rA=Vector2.sub(contactPoint,bodyA.position);
//这两个地方没有找到是哪里用到的?????
var rB=Vector2.sub(contactPoint,bodyB.position);
var vrn=Vector2.sub(vA,vB).dot(normal);
//这里得到的是Va相对于vB的速度向量与两球的圆心的单位向量相乘。
///a*b=|a|*|b|*cos@.所以如果vrn大于零,则夹角小于90度。
if(isTouchingvrn0){
//这里是冲量公式的一个部分
var normalMass=1/(1/bodyA.mass+1/bodyB.mass);
var restitution=(bodyA.restitution+bodyB.restitution)/2;
var normalImpulse=-normalMass*vrn*(1+restitution);
bodyA.speed.x+=normalImpulse*normal.x/bodyA.mass;
//这里总之是一个大球一个小球,所以速度一个增大一个减小
bodyA.speed.y+=normalImpulse*normal.y/bodyA.mass;
bodyB.speed.x-=normalImpulse*normal.x/bodyB.mass;
bodyB.speed.y-=normalImpulse*normal.y/bodyB.mass;
}
}
moveAsync2().start();
/script
/body
/html
下面详细解释都在源码中:
!doctype htmlhtmlheadmeta charset="utf-8"title无标题文档/titlestyle#canvas{ background:#eee;}/style/headbodycanvas id='canvas' width="500" height='500'/canvasscriptwindow.onload=function(){ var canvas = document.getElementById('canvas'); var context = canvas.getContext('2d'); //平移,主要是将坐标轴平移到中间,为了画圆定位方便 context.translate(250,250); //定义焦距 var fos = 300; //存放小球的数组 var arr = []; for(var i = 0 ; i 8 ; i++){ var arcObj = { //半径,用随机数目的是让每个小球大小不一 r:10+5*Math.random(), //起始X坐标 x:-200+i*30, //起始Y坐标 y:-100+200*Math.random(), //起始Z坐标,这里需要理解,我们要构造的是一个三维立体小球的运动 //则X,Y轴不能表达空间感,你需要充分的想象Z坐标轴是顺着你的眼睛的就是Z轴 z:i*10, //小球的运行速度 speed:20 } arr.push(arcObj); } setInterval(function(){ //清除画布,每次画之前先将上次的清除掉.然后绘出本次的,就可以形成动画效果. context.clearRect(-250,-250,500,500); //将arr排序,sort()的参数则是作为一种比较规则 var newArr = arr.sort(function (a,b){ return a.z b.z }); //循环绘出刚才定义的几个小球 for(var i = 0 ; i newArr.length ; i++){ //z轴的变化,每次变化都是 速度*时间+z = z;由于speed未定义单位,则时间可忽略 arr[i].z += arr[i].speed; //让小球来回弹跳 if(arr[i].z 600 || arr[i].z -50){ arr[i].speed *= -1; } //这里的缩放比例,一定要注意,你要想象你眼前有个球垂直从远处飞来,逐渐变大的过程,Z轴不断增加.焦距就想象成从最初你到球的距离,通过运动后,现在到球的距离和焦距就可以形成缩放比例. var scales = fos/(fos+arr[i].z); var x1 = arr[i].x*scales; var y1 = arr[i].y*scales; //保存之前的context绘图环境,即后续可以用context.restore方法可以恢复, //目的是让下面的context变化不影响其他的画图样式. context.save(); //平移X,Y 也可以不用平移X,Y只要在下面的画圆中定义相应的X,Y也能达到相同的目的 context.translate(x1,y1); //将坐标轴缩放,目的是让小球的大小发生视觉上的变化. context.scale(scales,scales); context.beginPath(); //定义放射性颜色渐变 var colorObj = context.createRadialGradient(0,0,0,0,0,arr[i].r); colorObj.addColorStop(0,'#cbc0f3'); colorObj.addColorStop(1,'#06198b'); context.fillStyle=colorObj; context.arc(0,0,arr[i].r,0,Math.PI*2); context.fill(); context.restore(); } },50);}/script/body/html
主要要理解焦距的概念,实际开发过程中,可能X轴,Y轴都有小球的运动速度分量,那才能在运动的过程中转弯,撞墙等特效.
如果是固定轨迹的话,是需要将所有的坐标都push到数组里,
如果不是固定的,只是希望互不影响,你可以用随机数做。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流