HTML5 Canvas圆形与方形元素碰撞弹跳及旋转效果开发需求
嘿,我帮你搞定了这个Canvas弹跳碰撞的需求,结合你提到的参考思路,我扩展了方形元素和碰撞旋转的逻辑,下面是完整的实现方案:
实现思路概述
要完成这个效果,核心要处理四个关键点:
- 给圆形和方形元素赋予基础的运动物理(速度、位置更新)
- 边界碰撞反弹逻辑(碰到画布边缘改变运动方向)
- 不同元素间的碰撞检测(圆-圆、圆-方)
- 碰撞触发的旋转效果(碰撞时给元素添加旋转角度增量)
完整代码示例
<!DOCTYPE html> <html> <head> <title>Canvas Bounce & Collide with Rotation</title> <style> canvas { border: 2px solid #333; } </style> </head> <body> <canvas id="my_canvas" width="600" height="300"></canvas> <script> const canvas = document.getElementById("my_canvas"); const c = canvas.getContext("2d"); const container = { x: 0, y: 0, width: canvas.width, height: canvas.height }; // 圆形元素类 class Circle { constructor(x, y, radius, color) { this.x = x; this.y = y; this.radius = radius; this.color = color; this.vx = (Math.random() - 0.5) * 4; this.vy = (Math.random() - 0.5) * 4; this.rotation = 0; // 旋转角度 } draw() { c.save(); c.translate(this.x, this.y); c.rotate(this.rotation); c.beginPath(); c.arc(0, 0, this.radius, 0, Math.PI * 2); c.fillStyle = this.color; c.fill(); c.restore(); } update() { // 边界碰撞反弹 if (this.x + this.radius > container.width || this.x - this.radius < container.x) { this.vx = -this.vx; this.rotation += (Math.random() - 0.5) * 0.2; // 碰撞时旋转 } if (this.y + this.radius > container.height || this.y - this.radius < container.y) { this.vy = -this.vy; this.rotation += (Math.random() - 0.5) * 0.2; } this.x += this.vx; this.y += this.vy; this.draw(); } } // 方形元素类 class Square { constructor(x, y, size, color) { this.x = x; this.y = y; this.size = size; this.color = color; this.vx = (Math.random() - 0.5) * 3; this.vy = (Math.random() - 0.5) * 3; this.rotation = 0; } draw() { c.save(); c.translate(this.x, this.y); c.rotate(this.rotation); c.fillStyle = this.color; c.fillRect(-this.size/2, -this.size/2, this.size, this.size); c.restore(); } update() { // 边界碰撞反弹 if (this.x + this.size/2 > container.width || this.x - this.size/2 < container.x) { this.vx = -this.vx; this.rotation += (Math.random() - 0.5) * 0.3; } if (this.y + this.size/2 > container.height || this.y - this.size/2 < container.y) { this.vy = -this.vy; this.rotation += (Math.random() - 0.5) * 0.3; } this.x += this.vx; this.y += this.vy; this.draw(); } } // 初始化元素数组 const elements = []; // 添加5个圆形 for (let i = 0; i < 5; i++) { const radius = 20 + Math.random() * 15; elements.push(new Circle( radius + Math.random() * (container.width - radius*2), radius + Math.random() * (container.height - radius*2), radius, `hsl(${Math.random()*360}, 70%, 60%)` )); } // 添加3个方形 for (let i = 0; i < 3; i++) { const size = 30 + Math.random() * 20; elements.push(new Square( size/2 + Math.random() * (container.width - size), size/2 + Math.random() * (container.height - size), size, `hsl(${Math.random()*360}, 80%, 50%)` )); } // 碰撞检测函数 function checkCollisions() { for (let i = 0; i < elements.length; i++) { for (let j = i + 1; j < elements.length; j++) { const el1 = elements[i]; const el2 = elements[j]; if (el1 instanceof Circle && el2 instanceof Circle) { // 圆-圆碰撞检测 const dx = el2.x - el1.x; const dy = el2.y - el1.y; const distance = Math.sqrt(dx*dx + dy*dy); const minDist = el1.radius + el2.radius; if (distance < minDist) { // 计算碰撞后的速度(简化的弹性碰撞) const tempVx = el1.vx; const tempVy = el1.vy; el1.vx = el2.vx; el2.vx = tempVx; el1.vy = el2.vy; el2.vy = tempVy; // 碰撞后旋转 el1.rotation += (Math.random() - 0.5) * 0.4; el2.rotation += (Math.random() - 0.5) * 0.4; // 分离重叠元素 const overlap = minDist - distance; const tx = dx / distance * overlap * 0.5; const ty = dy / distance * overlap * 0.5; el1.x -= tx; el1.y -= ty; el2.x += tx; el2.y += ty; } } else if ((el1 instanceof Circle && el2 instanceof Square) || (el1 instanceof Square && el2 instanceof Circle)) { // 圆-方碰撞检测(轴对齐简化版) const circle = el1 instanceof Circle ? el1 : el2; const square = el1 instanceof Square ? el1 : el2; // 找到圆到方形最近的点 const closestX = Math.max(square.x - square.size/2, Math.min(circle.x, square.x + square.size/2)); const closestY = Math.max(square.y - square.size/2, Math.min(circle.y, square.y + square.size/2)); // 计算圆中心到最近点的距离 const dx = circle.x - closestX; const dy = circle.y - closestY; const distance = Math.sqrt(dx*dx + dy*dy); if (distance < circle.radius) { // 反弹速度 circle.vx = -circle.vx; circle.vy = -circle.vy; square.vx = -square.vx * 0.8; square.vy = -square.vy * 0.8; // 碰撞旋转 circle.rotation += (Math.random() - 0.5) * 0.5; square.rotation += (Math.random() - 0.5) * 0.6; // 分离重叠 const overlap = circle.radius - distance; const tx = dx / distance * overlap; const ty = dy / distance * overlap; circle.x += tx; circle.y += ty; } } } } } // 动画循环 function animate() { c.clearRect(0, 0, container.width, container.height); elements.forEach(el => el.update()); checkCollisions(); requestAnimationFrame(animate); } animate(); </script> </body> </html>
关键功能解析
- 元素类设计:分别创建了
Circle和Square类,封装了位置、速度、旋转角度等属性,以及draw()(绘制元素,带旋转)和update()(更新位置、处理边界碰撞)方法。 - 边界碰撞处理:当元素碰到画布边缘时,反转速度方向,同时随机添加旋转角度增量,模拟碰撞后的旋转效果。
- 圆-圆碰撞:通过计算两个圆心的距离判断是否碰撞,碰撞后交换速度(简化弹性碰撞),并添加旋转,最后调整位置分离重叠元素。
- 圆-方碰撞:找到圆到方形边界的最近点,计算距离判断碰撞,碰撞后反转两者速度(方形速度做了衰减更真实),同时添加旋转效果,同样处理重叠分离。
内容的提问来源于stack exchange,提问作者Joe Cory
相关产品推荐
相关产品推荐

