You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 00:31:15