Search code examples
javascripthtmlanimationcanvaspolygon

Inward Circular Orbit - Canvas


I have a polygon that has circles on its vertices.
enter image description here

What I expect to accomplish is that every circle will be moving to the circle on its right. This is a physics concept which proves that if every circle is moving to the one on its right with a constant speed, soon they will reach the center. I'm trying to accomplish this animation, however I am able to move circles but not in the direction to the one next to it. Here's my current code that draws the polygon with circles:

function particleGenerator(n){
        const ctx = document.getElementById('poly').getContext('2d');
        ctx.reset();
        drawPolygon(ctx, 154, 71.25 , n, 50, 0, 5, 7.5);
}
  
const drawPolygon = (ctx, x, y, points, radius, rotation = 0, nodeSize = 0, nodeInset = 0) => {
    ctx.beginPath();
    ctx.moveTo(
        x + radius * Math.cos(rotation),
        y + radius * Math.sin(rotation)
    );          
    for (let i = 1; i <= points; i += 1) {
        const angle = (i * (2 * Math.PI / points)) + rotation;
        ctx.lineTo(
            x + radius * Math.cos(angle),
            y + radius * Math.sin(angle)
        );
    }
    ctx.fillStyle = "#00818A";
    ctx.fill();
    if (!nodeSize) return;
    const dist = radius - nodeInset;
    for (let i = 1; i <= points; i += 1) {
        const angle = (i * (2 * Math.PI / points)) + rotation;
        let x1 = x + dist * Math.cos(angle);
        let y1 = y + dist * Math.sin(angle);
        ctx.beginPath();         
        ctx.arc(x1, y1, nodeSize, 0, 2 * Math.PI);
        ctx.fillStyle = "#DBEDF3"
        ctx.fill();
    }
};
<button onclick="particleGenerator(4)">Click Me!</button>
<canvas id="poly">


Solution

  • You can keep track of a list of corners. You generate them in order, so to get a corner's next neighbor you can do corners[i + 1] || corners[0].

    To move the corner in the direction of the next one, you can calculate their differences in x and y coordinates and add a percentage of that difference to a corner's current location.

    Here's a running example (I did remove some of the code so I could focus on just the updating problem:

    function particleGenerator(n) {
      const ctx = document.getElementById('poly').getContext('2d');
      ctx.reset();
      const originalCorners = createCorners(150, 70, n, 50);
      const corners = createCorners(150, 70, n, 50);
    
      const next = () => {
        corners.forEach(([x0, y0], i) => {
          const [x1, y1] = corners[i + 1] || corners[0];
          
          const dx = x1 - x0;
          const dy = y1 - y0;
          
          const SPEED = 0.05;
          corners[i][0] = x0 + dx * SPEED;
          corners[i][1] = y0 + dy * SPEED;
        });
      }
      
      const frame = () => {
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
      
        drawPolygon(ctx, originalCorners, "grey");
        drawPolygon(ctx, corners);
        drawDots(ctx, corners);
        
        next();
        requestAnimationFrame(frame);
      };
      
      frame();
        
    }
    
    
    const createCorners = (x, y, n, radius) => {
      const corners = [];
      
      for (let i = 1; i <= n; i += 1) {
        const angle = (i * (2 * Math.PI / n));
        corners.push([
          x + radius * Math.cos(angle),
          y + radius * Math.sin(angle)
        ]);
      }
      
      return corners;
    }
    
    
    
    const drawPolygon = (
      ctx,
      corners,
      color = "#00818A"
    ) => {
      
      // Draw fill
      ctx.beginPath();
      corners.forEach((c, i) => {
        if (i === 0) ctx.moveTo(...c);
        else ctx.lineTo(...c);
      });
      ctx.fillStyle = color
      ctx.fill();
    };
    
    const drawDots = (
      ctx,
      corners,
    ) => {
      // Draw dots
      corners.forEach(([x, y], i, all) => {
        ctx.beginPath();
        ctx.arc(x, y, 5, 0, 2 * Math.PI);
        ctx.fillStyle = "red"
        ctx.fill();
      });
    };
    <input type="number" value="6" min="3" max="100">
    <button onclick="particleGenerator(document.querySelector('input').valueAsNumber)">Click Me!</button>
    <canvas id="poly">