vue怎么实现小球碰撞
Vue 实现小球碰撞的方法
在 Vue 中实现小球碰撞效果,可以通过 Canvas 或 DOM 结合 JavaScript 的物理引擎来实现。以下是一个基于 Canvas 的实现方案。

创建 Vue 组件
在 Vue 项目中创建一个组件,用于绘制和更新小球的状态。
<template>
<div>
<canvas ref="canvas" :width="width" :height="height"></canvas>
</div>
</template>
<script>
export default {
data() {
return {
width: 800,
height: 600,
balls: [],
ctx: null,
};
},
mounted() {
this.initCanvas();
this.createBalls();
this.animate();
},
methods: {
initCanvas() {
const canvas = this.$refs.canvas;
this.ctx = canvas.getContext('2d');
},
createBalls() {
for (let i = 0; i < 10; i++) {
this.balls.push({
x: Math.random() * this.width,
y: Math.random() * this.height,
radius: Math.random() * 20 + 10,
dx: (Math.random() - 0.5) * 4,
dy: (Math.random() - 0.5) * 4,
color: `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255})`,
});
}
},
animate() {
requestAnimationFrame(this.animate);
this.ctx.clearRect(0, 0, this.width, this.height);
this.updateBalls();
this.drawBalls();
},
updateBalls() {
this.balls.forEach((ball, index) => {
ball.x += ball.dx;
ball.y += ball.dy;
// 边界碰撞检测
if (ball.x + ball.radius > this.width || ball.x - ball.radius < 0) {
ball.dx = -ball.dx;
}
if (ball.y + ball.radius > this.height || ball.y - ball.radius < 0) {
ball.dy = -ball.dy;
}
// 小球之间的碰撞检测
for (let j = index + 1; j < this.balls.length; j++) {
const otherBall = this.balls[j];
const dx = ball.x - otherBall.x;
const dy = ball.y - otherBall.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < ball.radius + otherBall.radius) {
// 碰撞响应
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.dx * cos + ball.dy * sin;
const vy1 = ball.dy * cos - ball.dx * sin;
const vx2 = otherBall.dx * cos + otherBall.dy * sin;
const vy2 = otherBall.dy * cos - otherBall.dx * sin;
// 碰撞后的速度(一维弹性碰撞)
const vx1Final = ((ball.radius - otherBall.radius) * vx1 + 2 * otherBall.radius * vx2) / (ball.radius + otherBall.radius);
const vx2Final = ((otherBall.radius - ball.radius) * vx2 + 2 * ball.radius * vx1) / (ball.radius + otherBall.radius);
// 更新速度
ball.dx = vx1Final * cos - vy1 * sin;
ball.dy = vy1 * cos + vx1Final * sin;
otherBall.dx = vx2Final * cos - vy2 * sin;
otherBall.dy = vy2 * cos + vx2Final * sin;
// 防止小球重叠
const overlap = ball.radius + otherBall.radius - distance;
const moveX = overlap * cos * 0.5;
const moveY = overlap * sin * 0.5;
ball.x += moveX;
ball.y += moveY;
otherBall.x -= moveX;
otherBall.y -= moveY;
}
}
});
},
drawBalls() {
this.balls.forEach(ball => {
this.ctx.beginPath();
this.ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
this.ctx.fillStyle = ball.color;
this.ctx.fill();
this.ctx.closePath();
});
},
},
};
</script>
<style>
canvas {
border: 1px solid #000;
}
</style>
物理引擎优化
如果需要更复杂的物理效果,可以引入物理引擎如 Matter.js 或 p5.js。
import Matter from 'matter-js';
export default {
mounted() {
const engine = Matter.Engine.create();
const render = Matter.Render.create({
element: this.$refs.canvas,
engine: engine,
options: {
width: this.width,
height: this.height,
wireframes: false,
},
});
const balls = [];
for (let i = 0; i < 10; i++) {
balls.push(Matter.Bodies.circle(
Math.random() * this.width,
Math.random() * this.height,
Math.random() * 20 + 10,
{
restitution: 0.8,
render: {
fillStyle: `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255})`,
},
}
));
}
Matter.Composite.add(engine.world, balls);
Matter.Engine.run(engine);
Matter.Render.run(render);
},
};
关键点说明
- 使用 Canvas 绘制小球,并通过
requestAnimationFrame实现动画循环。 - 边界碰撞检测通过判断小球是否超出画布边界实现。
- 小球之间的碰撞检测通过计算距离和半径之和实现。
- 碰撞响应通过旋转坐标系简化为一维弹性碰撞问题。
通过以上方法,可以在 Vue 中实现小球的碰撞效果。







