canvas实现动态小球碰撞
时间: 2023-08-03 17:32:18 浏览: 132
html5 canvas实现的鼠标响应式模拟小球互相碰撞动画特效源码.zip
实现动态小球碰撞需要用到Canvas的2D绘图API,具体步骤如下:
1. 创建Canvas元素和2D绘图上下文
```html
<canvas id="canvas"></canvas>
```
```javascript
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
```
2. 定义小球对象
```javascript
class Ball {
constructor(x, y, r, vx, vy, color) {
this.x = x; // 圆心x坐标
this.y = y; // 圆心y坐标
this.r = r; // 半径
this.vx = vx; // 水平速度
this.vy = vy; // 垂直速度
this.color = color; // 颜色
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
}
}
```
3. 创建多个小球对象,并绘制到Canvas上
```javascript
const balls = []; // 存储小球对象的数组
const colors = ['#ff0000', '#00ff00', '#0000ff']; // 小球颜色
for (let i = 0; i < 3; i++) {
const ball = new Ball(
Math.random() * canvas.width, // 随机x坐标
Math.random() * canvas.height, // 随机y坐标
20, // 半径
Math.random() * 4 - 2, // 随机水平速度
Math.random() * 4 - 2, // 随机垂直速度
colors[i] // 随机颜色
);
balls.push(ball);
}
function drawBalls() {
balls.forEach(ball => ball.draw());
}
drawBalls();
```
4. 实现小球碰撞
```javascript
function updateBalls() {
balls.forEach(ball => {
ball.x += ball.vx;
ball.y += ball.vy;
// 碰到边界反弹
if (ball.x < ball.r || ball.x > canvas.width - ball.r) {
ball.vx = -ball.vx;
}
if (ball.y < ball.r || ball.y > canvas.height - ball.r) {
ball.vy = -ball.vy;
}
// 碰撞检测
balls.forEach(otherBall => {
if (ball !== otherBall) {
const dx = ball.x - otherBall.x;
const dy = ball.y - otherBall.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < ball.r + otherBall.r) {
// 碰撞
const angle = Math.atan2(dy, dx);
const sin = Math.sin(angle);
const cos = Math.cos(angle);
// 旋转坐标系
const x1 = 0;
const y1 = 0;
const x2 = dx * cos + dy * sin;
const y2 = dy * cos - dx * sin;
// 旋转速度向量
const vx1 = ball.vx * cos + ball.vy * sin;
const vy1 = ball.vy * cos - ball.vx * sin;
const vx2 = otherBall.vx * cos + otherBall.vy * sin;
const vy2 = otherBall.vy * cos - otherBall.vx * sin;
// 碰撞后的速度向量
const vx1Final = ((ball.r - otherBall.r) * vx1 + 2 * otherBall.r * vx2) / (ball.r + otherBall.r);
const vx2Final = ((otherBall.r - ball.r) * vx2 + 2 * ball.r * vx1) / (ball.r + otherBall.r);
const vy1Final = vy1;
const vy2Final = vy2;
// 旋转回原坐标系
const x1Final = x1 * cos - y1 * sin;
const y1Final = y1 * cos + x1 * sin;
const x2Final = x2 * cos - y2 * sin;
const y2Final = y2 * cos + x2 * sin;
// 更新位置和速度向量
ball.x = ball.x + (x1Final - x1);
ball.y = ball.y + (y1Final - y1);
ball.vx = vx1Final * cos - vy1Final * sin;
ball.vy = vy1Final * cos + vx1Final * sin;
otherBall.x = ball.x + (x2Final - x1);
otherBall.y = ball.y + (y2Final - y1);
otherBall.vx = vx2Final * cos - vy2Final * sin;
otherBall.vy = vy2Final * cos + vx2Final * sin;
}
}
});
});
}
```
5. 清空Canvas并更新小球位置
```javascript
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function loop() {
clearCanvas();
updateBalls();
drawBalls();
requestAnimationFrame(loop);
}
loop();
```
完整代码如下:
```html
<canvas id="canvas"></canvas>
```
```javascript
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
class Ball {
constructor(x, y, r, vx, vy, color) {
this.x = x; // 圆心x坐标
this.y = y; // 圆心y坐标
this.r = r; // 半径
this.vx = vx; // 水平速度
this.vy = vy; // 垂直速度
this.color = color; // 颜色
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
}
}
const balls = []; // 存储小球对象的数组
const colors = ['#ff0000', '#00ff00', '#0000ff']; // 小球颜色
for (let i = 0; i < 3; i++) {
const ball = new Ball(
Math.random() * canvas.width, // 随机x坐标
Math.random() * canvas.height, // 随机y坐标
20, // 半径
Math.random() * 4 - 2, // 随机水平速度
Math.random() * 4 - 2, // 随机垂直速度
colors[i] // 随机颜色
);
balls.push(ball);
}
function drawBalls() {
balls.forEach(ball => ball.draw());
}
function updateBalls() {
balls.forEach(ball => {
ball.x += ball.vx;
ball.y += ball.vy;
// 碰到边界反弹
if (ball.x < ball.r || ball.x > canvas.width - ball.r) {
ball.vx = -ball.vx;
}
if (ball.y < ball.r || ball.y > canvas.height - ball.r) {
ball.vy = -ball.vy;
}
// 碰撞检测
balls.forEach(otherBall => {
if (ball !== otherBall) {
const dx = ball.x - otherBall.x;
const dy = ball.y - otherBall.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < ball.r + otherBall.r) {
// 碰撞
const angle = Math.atan2(dy, dx);
const sin = Math.sin(angle);
const cos = Math.cos(angle);
// 旋转坐标系
const x1 = 0;
const y1 = 0;
const x2 = dx * cos + dy * sin;
const y2 = dy * cos - dx * sin;
// 旋转速度向量
const vx1 = ball.vx * cos + ball.vy * sin;
const vy1 = ball.vy * cos - ball.vx * sin;
const vx2 = otherBall.vx * cos + otherBall.vy * sin;
const vy2 = otherBall.vy * cos - otherBall.vx * sin;
// 碰撞后的速度向量
const vx1Final = ((ball.r - otherBall.r) * vx1 + 2 * otherBall.r * vx2) / (ball.r + otherBall.r);
const vx2Final = ((otherBall.r - ball.r) * vx2 + 2 * ball.r * vx1) / (ball.r + otherBall.r);
const vy1Final = vy1;
const vy2Final = vy2;
// 旋转回原坐标系
const x1Final = x1 * cos - y1 * sin;
const y1Final = y1 * cos + x1 * sin;
const x2Final = x2 * cos - y2 * sin;
const y2Final = y2 * cos + x2 * sin;
// 更新位置和速度向量
ball.x = ball.x + (x1Final - x1);
ball.y = ball.y + (y1Final - y1);
ball.vx = vx1Final * cos - vy1Final * sin;
ball.vy = vy1Final * cos + vx1Final * sin;
otherBall.x = ball.x + (x2Final - x1);
otherBall.y = ball.y + (y2Final - y1);
otherBall.vx = vx2Final * cos - vy2Final * sin;
otherBall.vy = vy2Final * cos + vx2Final * sin;
}
}
});
});
}
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function loop() {
clearCanvas();
updateBalls();
drawBalls();
requestAnimationFrame(loop);
}
loop();
```
阅读全文