使用html写一个网页,其中多个小球在六边形区域内运动,碰到边界就反弹,只写一个html文件即可。
Creato il: 14 febbraio 2025
Creato il: 14 febbraio 2025
使用html写一个网页,其中多个小球在六边形区域内运动,碰到边界就反弹,只写一个html文件即可。
下面给出一个只用一个 HTML 文件即可运行的示例。其中使用 <canvas>
元素绘制一个正六边形区域,并让多个小球在其中运动,碰到边界自动反弹。将以下代码保存为一个 .html
文件后,直接在浏览器中打开即可查看效果。
html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>多个小球在六边形区域内运动</title> <style> body { margin: 0; padding: 0; } canvas { display: block; margin: 0 auto; background: #333; /* 画布背景色,可自行调整 */ } </style> </head> <body> <canvas id="myCanvas" width="800" height="600"></canvas> <script> const canvas = document.getElementById("myCanvas"); const ctx = canvas.getContext("2d"); const W = canvas.width; const H = canvas.height; // 六边形的中心和外接圆半径 const centerX = W / 2; const centerY = H / 2; const R = 200; // 六边形半径 // 计算正六边形6个顶点坐标(逆时针) let hexPoints = []; for (let i = 0; i < 6; i++) { let angle = (Math.PI / 3) * i; // 每个顶点相隔60度 let x = centerX + R * Math.cos(angle); let y = centerY + R * Math.sin(angle); hexPoints.push({ x, y }); } // 根据顶点信息创建6条边的描述 // 每条边保存:起点坐标、单位外法向量(nx, ny)以及用于计算点到线距离的offset function createEdges(points) { let edges = []; for (let i = 0; i < points.length; i++) { let p1 = points[i]; let p2 = points[(i + 1) % points.length]; // 线段方向向量 let dx = p2.x - p1.x; let dy = p2.y - p1.y; let length = Math.sqrt(dx * dx + dy * dy); // 通过将方向向量旋转90度(+90度)得到外法向量 // 若六边形点是逆时针定义,则+90度方向在多边形外侧 let nx = dy / length; let ny = -dx / length; // 点到直线距离的计算公式: n · p - offset // 其中 offset = n · p1 edges.push({ x1: p1.x, y1: p1.y, nx: nx, ny: ny, offset: nx * p1.x + ny * p1.y }); } return edges; } let edges = createEdges(hexPoints); // 定义小球类 class Ball { constructor(x, y, vx, vy, r, color) { this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.r = r; this.color = color; } update() { // 更新小球位置 this.x += this.vx; this.y += this.vy; // 检测与六边形各边的碰撞并反射 for (let edge of edges) { // 点到直线的有符号距离: (nx * x + ny * y - offset) let dist = edge.nx * this.x + edge.ny * this.y - edge.offset; // dist > 0 说明在外侧;dist < 0 在内侧(根据外法向量) // 如果小球中心到边缘距离 dist < r,则说明碰撞 if (dist > 0 && dist < this.r) { // 在外侧并且已经越过/贴近边界,需要反射 // 反射公式:v' = v - 2*(v·n)*n let dot = this.vx * edge.nx + this.vy * edge.ny; this.vx -= 2 * dot * edge.nx; this.vy -= 2 * dot * edge.ny; // 调整位置,使小球重新回到边界内侧,避免卡在边上 let penetration = this.r - dist; this.x -= penetration * edge.nx; this.y -= penetration * edge.ny; } } } draw(ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); ctx.closePath(); } } // 随机生成一些小球 let balls = []; let colors = ["red", "green", "blue", "yellow", "white", "cyan", "magenta", "orange"]; for (let i = 0; i < 10; i++) { let angle = Math.random() * Math.PI * 2; let speed = 2 + Math.random() * 2; // 速度在2~4之间 let vx = speed * Math.cos(angle); let vy = speed * Math.sin(angle); let rad = 10; // 小球半径 // 在六边形内部随机初始化小球位置(这里简单处理为中心附近) let x = centerX + Math.random() * R * 0.5 * Math.cos(angle); let y = centerY + Math.random() * R * 0.5 * Math.sin(angle); let color = colors[Math.floor(Math.random() * colors.length)]; balls.push(new Ball(x, y, vx, vy, rad, color)); } // 动画更新 function animate() { ctx.clearRect(0, 0, W, H); // 先绘制出六边形区域 ctx.beginPath(); ctx.moveTo(hexPoints[0].x, hexPoints[0].y); for (let i = 1; i < hexPoints.length; i++) { ctx.lineTo(hexPoints[i].x, hexPoints[i].y); } ctx.closePath(); ctx.strokeStyle = "white"; ctx.lineWidth = 2; ctx.stroke(); // 更新并绘制所有小球 for (let ball of balls) { ball.update(); ball.draw(ctx); } requestAnimationFrame(animate); } animate(); </script> </body> </html>
主要思路:
绘制六边形
(centerX, centerY)
为中心,计算六个顶点坐标并存储到 hexPoints
中。canvas
上用 moveTo
/ lineTo
来绘制线条并闭合。计算边界碰撞
dist
。如果 dist
大于 0 且小于小球半径,则表示小球越过了边界,需要对速度进行反射。penetration = r - dist
对位置做一次修正,将小球推回边界内。动画实现
requestAnimationFrame
进行持续绘制更新:
将上述代码保存后,在浏览器打开就能看到多个小球在六边形区域内来回弹跳的动画效果。你可以根据需要调整小球数量、半径、速度范围、颜色或六边形大小等参数。