### 自由落體須知
當一個籃球下落時會受重力作用和與空氣的摩擦力,每次彈起落下籃球都會消耗能量,最終直到小球停止地面。
在 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>
```