Canvas实现内向圆周轨道动画:顶点圆向右侧圆移动问题
解决方案
要实现每个圆形向右侧相邻圆形移动的动画,核心是保存粒子状态并在动画循环中持续更新位置,以下是修改后的完整代码及关键逻辑说明:
1. 重构粒子状态管理
首先需要把每个粒子的位置存储为可修改的状态,而非每次绘制时重新计算初始位置。用数组保存所有粒子的坐标:
// 全局变量存储粒子状态 let particles = []; let animationId = null; const centerX = 154; const centerY = 71.25; const moveSpeed = 1; // 粒子移动速度 function initParticles(n) { particles = []; const radius = 50; const nodeInset = 7.5; const dist = radius - nodeInset; for (let i = 1; i <= n; i += 1) { const angle = (i * (2 * Math.PI / n)); const x = centerX + dist * Math.cos(angle); const y = centerY + dist * Math.sin(angle); particles.push({ x, y }); } }
2. 实现动画循环与位置更新
使用requestAnimationFrame创建持续动画循环,每次循环完成以下操作:
- 清空画布
- 绘制多边形背景
- 更新每个粒子的位置(向右侧相邻粒子移动)
- 绘制所有粒子
function animate() { const ctx = document.getElementById('poly').getContext('2d'); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // 绘制多边形背景 drawPolygonBackground(ctx, centerX, centerY, particles.length, 50); // 更新并绘制粒子 const updatedParticles = []; particles.forEach((particle, index) => { // 确定右侧相邻粒子索引(最后一个粒子的右侧是第一个) const nextIndex = (index + 1) % particles.length; const nextParticle = particles[nextIndex]; // 计算方向向量并归一化 const dx = nextParticle.x - particle.x; const dy = nextParticle.y - particle.y; const distance = Math.sqrt(dx*dx + dy*dy); // 粒子重合时停止移动,避免除以0 if (distance < 0.1) { updatedParticles.push({...particle}); return; } // 计算位移并更新位置 const moveX = (dx / distance) * moveSpeed; const moveY = (dy / distance) * moveSpeed; updatedParticles.push({ x: particle.x + moveX, y: particle.y + moveY }); // 绘制当前粒子 ctx.beginPath(); ctx.arc(particle.x, particle.y, 5, 0, 2 * Math.PI); ctx.fillStyle = "#DBEDF3"; ctx.fill(); }); particles = updatedParticles; // 所有粒子接近中心时停止动画 const allAtCenter = particles.every(p => Math.sqrt((p.x - centerX)**2 + (p.y - centerY)**2) < 0.5 ); if (!allAtCenter) { animationId = requestAnimationFrame(animate); } } // 单独提取多边形背景绘制函数 function drawPolygonBackground(ctx, x, y, points, radius) { ctx.beginPath(); ctx.moveTo(x + radius * Math.cos(0), y + radius * Math.sin(0)); for (let i = 1; i <= points; i += 1) { const angle = (i * (2 * Math.PI / points)); ctx.lineTo(x + radius * Math.cos(angle), y + radius * Math.sin(angle)); } ctx.fillStyle = "#00818A"; ctx.fill(); }
3. 修改触发函数
更新particleGenerator函数,初始化粒子并启动动画:
function particleGenerator(n){ // 停止之前的动画(如果存在) if (animationId) { cancelAnimationFrame(animationId); } const ctx = document.getElementById('poly').getContext('2d'); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // 初始化粒子并启动动画 initParticles(n); animate(); }
4. HTML部分调整
给canvas设置合适尺寸:
<button onclick="particleGenerator(4)">生成4边形动画</button> <canvas id="poly" width="300" height="150"></canvas>
关键逻辑说明
- 粒子状态存储:用数组保存每个粒子的实时坐标,确保动画过程中能持续更新。
- 方向计算:通过右侧相邻粒子的坐标差得到方向向量,归一化后保证移动速度恒定(每帧移动像素数一致)。
- 动画终止条件:当所有粒子都接近中心时停止循环,避免无意义的计算。
内容的提问来源于stack exchange,提问作者The Myth
相关产品推荐
相关产品推荐

