// 初始化Canvas
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// 设置Canvas实际大小
canvas.width = 800;
canvas.height = 600;
// 球体数组
const balls = [];
const colors = ['#FF5252', '#FF4081', '#E040FB', '#7C4DFF', '#536DFE', '#448AFF', '#40C4FF', '#18FFFF', '#64FFDA', '#69F0AE', '#B2FF59', '#EEFF41', '#FFFF00', '#FFD740', '#FFAB40', '#FF6E40'];
// 球体类
class Ball {
constructor(x, y, radius) {
this.x = x;
this.y = y;
this.radius = radius;
this.dx = (Math.random() - 0.5) * 8;
this.dy = (Math.random() - 0.5) * 8;
this.color = colors[Math.floor(Math.random() * colors.length)];
this.mass = radius * 0.1;
this.friction = 0.99;
this.trail = [];
this.maxTrailLength = 10;
}
// 绘制球体
draw() {
// 绘制轨迹
for (let i = 0; i < this.trail.length; i++) {
const alpha = i / this.trail.length * 0.6;
ctx.beginPath();
ctx.arc(this.trail[i].x, this.trail[i].y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color.replace(')', `, ${alpha})`).replace('rgb', 'rgba');
ctx.fill();
}
// 绘制球体
const gradient = ctx.createRadialGradient(
this.x - this.radius * 0.3,
this.y - this.radius * 0.3,
this.radius * 0.1,
this.x,
this.y,
this.radius
);
gradient.addColorStop(0, 'white');
gradient.addColorStop(1, this.color);
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 3;
ctx.shadowOffsetY = 3;
ctx.fill();
ctx.shadowColor = 'transparent';
}
// 更新球体位置
update() {
// 添加重力
this.dy += 0.2;
// 应用摩擦力
this.dx *= this.friction;
this.dy *= this.friction;
// 更新位置
this.x += this.dx;
this.y += this.dy;
// 记录轨迹
this.trail.push({x: this.x, y: this.y});
if (this.trail.length > this.maxTrailLength) {
this.trail.shift();
}
// 边界碰撞检测
if (this.x + this.radius > canvas.width) {
this.x = canvas.width - this.radius;
this.dx = -this.dx * 0.8;
} else if (this.x - this.radius < 0) {
this.x = this.radius;
this.dx = -this.dx * 0.8;
}
if (this.y + this.radius > canvas.height) {
this.y = canvas.height - this.radius;
this.dy = -this.dy * 0.8;
} else if (this.y - this.radius < 0) {
this.y = this.radius;
this.dy = -this.dy * 0.8;
}
}
}
// 球体碰撞检测
function checkCollision(ball1, ball2) {
const dx = ball2.x - ball1.x;
const dy = ball2.y - ball1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < ball1.radius + ball2.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 = ball1.dx * cos + ball1.dy * sin;
const vy1 = ball1.dy * cos - ball1.dx * sin;
const vx2 = ball2.dx * cos + ball2.dy * sin;
const vy2 = ball2.dy * cos - ball2.dx * sin;
// 碰撞后速度
const vx1Final = ((ball1.mass - ball2.mass) * vx1 + 2 * ball2.mass * vx2) / (ball1.mass + ball2.mass);
const vx2Final = ((ball2.mass - ball1.mass) * vx2 + 2 * ball1.mass * vx1) / (ball1.mass + ball2.mass);
// 更新位置防止重叠
const overlap = ball1.radius + ball2.radius - distance;
ball1.x -= overlap * cos * 0.5;
ball1.y -= overlap * sin * 0.5;
ball2.x += overlap * cos * 0.5;
ball2.y += overlap * sin * 0.5;
// 旋转回原坐标系
ball1.dx = vx1Final * cos - vy1 * sin;
ball1.dy = vy1 * cos + vx1Final * sin;
ball2.dx = vx2Final * cos - vy2 * sin;
ball2.dy = vy2 * cos + vx2Final * sin;
}
}
// 初始化球体
function initBalls(count) {
for (let i = 0; i < count; i++) {
const radius = Math.random() * 20 + 10;
const x = Math.random() * (canvas.width - radius * 2) + radius;
const y = Math.random() * (canvas.height - radius * 2) + radius;
balls.push(new Ball(x, y, radius));
}
}
// 动画循环
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 更新和绘制所有球体
for (let i = 0; i < balls.length; i++) {
balls[i].update();
balls[i].draw();
}
// 检测球体间碰撞
for (let i = 0; i < balls.length; i++) {
for (let j = i + 1; j < balls.length; j++) {
checkCollision(balls[i], balls[j]);
}
}
}
// 鼠标交互
let isDragging = false;
let draggedBall = null;
let offsetX = 0;
let offsetY = 0;
canvas.addEventListener('mousedown', (e) => {
const rect = canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
for (let i = 0; i < balls.length; i++) {
const ball = balls[i];
const distance = Math.sqrt((mouseX - ball.x) ** 2 + (mouseY - ball.y) ** 2);
if (distance < ball.radius) {
isDragging = true;
draggedBall = ball;
offsetX = mouseX - ball.x;
offsetY = mouseY - ball.y;
ball.trail = []; // 清除拖动时的轨迹
break;
}
}
});
canvas.addEventListener('mousemove', (e) => {
if (isDragging && draggedBall) {
const rect = canvas.getBoundingClientRect();
draggedBall.x = e.clientX - rect.left - offsetX;
draggedBall.y = e.clientY - rect.top - offsetY;
draggedBall.dx = (e.movementX || 0) * 0.5;
draggedBall.dy = (e.movementY || 0) * 0.5;
}
});
canvas.addEventListener('mouseup', () => {
isDragging = false;
draggedBall = null;
});
canvas.addEventListener('click', (e) => {
if (!isDragging) {
const rect = canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
const radius = Math.random() * 20 + 10;
balls.push(new Ball(mouseX, mouseY, radius));
}
});
// 启动游戏
initBalls(10);
animate();
评论