### 自由落体须知
当一个篮球下落时会受重力作用和与空气的摩擦力,每次弹起落下篮球都会消耗能量,最终直到小球停止地面。
在 canvas中我们需要模拟重力加速度和摩擦力(也就是小球消耗的能量来源)。
在canvas中主要思路:
1. 点击鼠标时记录鼠标坐标,并绘制小球,将生成的小球存储起来
2. 小球下落时,根据重力加速度计算小球位置,并将canvas清除,重新绘制。(没一帧绘制一次)
3. 检测小球是否到达底部,如果到达底部让小球位置向上移动,速度慢慢减少,同时因为要模拟能力损失,所以小球向上的位置不能回到上次下落时的初始位置
### 自由落体代码
```HTML
<canvas width="900" height="800">
您的浏览器不支持canvas
</canvas>
<script>
let canvas = document.querySelector('canvas')
let ctx=canvas.getContext("2d");
let ballAry = []; // 存储小球
let ballId = 0; // 小球id
let g = .5 // 定义重力加速度
let bounce = 0.8;//定义反弹系数;
let Flag = true; // 是否开启 requestAnimationFrame 标识,防止多次启动
canvas.onmouseup = function(e){
let x = e.pageX - canvas.getBoundingClientRect().left; // 获取小球初始x坐标
let y = e.pageY - canvas.getBoundingClientRect().top; // 获取小球初始y坐标
let radius = Math.floor(Math.random()*10 + 15) // 随机获取小球半径
new Ball(x,y,radius)
}
function Ball(x,y,radius){
this.x = x;
this.y = y;
this.vx = 0; // 小球初始x轴速率为0 自由下落时用不到,左右弹跳时用的到
this.vy = 0; // 小球初始x轴速率为0
this.radius = radius;
ballAry[ballId] = this // 将小球存在ballAry 数组中
this.id = ballId++;
this.init()
}
Ball.prototype.init = function(){ // 初始绘制小球并开启定时
this.draw()
if(Flag){
this.updata()
Flag = false;
}
}
Ball.prototype.draw = function(){ // 绘制小球
ctx.beginPath();
ctx.arc(this.x,this.y,this.radius,0,2*Math.PI);
ctx.fillStyle = "red"
ctx.fill();
ctx.closePath();
}
Ball.prototype.updataPosition = function(){ // 更新小球位置
if(this.y >= 800 - this.radius){ // 碰撞底部时
this.y = 800 - this.radius
// 小球与底部碰撞时,将速率变成负值,并减小0.8,这样模拟消失的能量
this.vy *= -bounce;
}
this.vy += g; // 当小球向下时加速度逐渐增加,向上时逐渐减小
this.y += this.vy // 小球y坐标位置,其实vy 是加速度,这里的加速度是每帧小球移动的距离
}
Ball.prototype.updata = function(){
ctx.clearRect(0, 0, 900, 800); // 清除canvas
ballAry.map(item=>{ // 对每一个小球进行定位,并绘制
item.updataPosition()
item.draw()
})
let animateFrame = requestAnimationFrame(arguments.callee); // 定时,每帧刷新一次
}
</script>
```
### 自由落体加左右碰撞
我们上边实现了小球的自由落体,下来我们要实现小球碰到四周进行弹跳的效果, 和自由落体不同的是,我们考虑到了x轴方向的速度,并且在鼠标移动时给小球添加一个横向的初始速度,该初始速度与鼠标开始时移动的距离成正比。
```HTML
<canvas width="900" height="800">
您的浏览器不支持canvas
</canvas>
<script>
let canvas = document.querySelector('canvas')
let ctx=canvas.getContext("2d");
let ballAry = [];
let ballId = 0;
let g = .5 // 定义重力加速度
let bounce = 0.7;//定义反弹系数;
let Flag = true; // 标识,只添加一次定时
let startPosition = [] // 存储鼠标开始坐标位置
let endPosition = [] // 存储鼠标结束坐标位置
let current = [] // 当前鼠标坐标
canvas.onmousedown = function(e){
startPosition[0] = e.pageX - canvas.getBoundingClientRect().left;
startPosition[1] = e.pageY - canvas.getBoundingClientRect().top;
}
canvas.onmousemove = function(e){
current[0] = e.pageX - canvas.getBoundingClientRect().left;
current[1] = e.pageY - canvas.getBoundingClientRect().top;
}
canvas.onmouseup = function(e){
let speed = []
endPosition[0] = e.pageX - canvas.getBoundingClientRect().left;
endPosition[1] = e.pageY - canvas.getBoundingClientRect().top;
speed[0] = (startPosition[0] - endPosition[0])/10
speed[1] = (startPosition[1] - endPosition[1])/10
let radius = Math.floor(Math.random()*10 + 15)
new Ball(current,speed,radius)
}
function Ball(position,speed,radius){
this.x = position[0];
this.y = position[1];
this.speedX = speed[0]; // 运动的x轴初始速度
this.speedY = speed[1]; // 运动的y轴初始速度
this.radius = radius;
ballAry[ballId] = this
this.id = ballId++;
this.init()
}
Ball.prototype.init = function(){
this.draw()
if(Flag){
this.updata()
Flag = false;
}
}
Ball.prototype.draw = function(){
ctx.beginPath();
ctx.arc(this.x,this.y,this.radius,0,2*Math.PI);
ctx.fillStyle = "red"
ctx.fill();
ctx.closePath();
}
Ball.prototype.checkCollide = function(){
if(this.y >= 800 - this.radius){ // 碰撞底部时
this.y = 800 - this.radius
this.speedY *= -bounce;
}else if(this.y <= this.radius){
this.y = this.radius
this.speedY *= -bounce
}
this.speedY += g;
// x轴左右碰撞
if(this.x >= 900 - this.radius){
this.x = 900 - this.radius
this.speedX *= -bounce
}else if(this.x <= this.radius){
this.x = this.radius
this.speedX *= -bounce
}
}
Ball.prototype.updataPosition = function(){
this.checkCollide()
this.x += this.speedX
this.y += this.speedY
}
Ball.prototype.updata = function(){
ctx.clearRect(0, 0, 900, 800);
ballAry.map(item=>{
item.updataPosition()
item.draw()
})
requestAnimationFrame(arguments.callee);
}
</script>
```