Search code examples
animationsvgpolygonsnap.svg

Animate each point of a polygon individually


I have a website-menu, which consists of multiple polygons, which the user can click on. Furthermore, the polygons share some of the nodes. So far so good (see the code below).

Now, I would like to animate these polygons in a way such that they dynamically change their shape in a slow and endless loop. To this end I need to animate each node individually. I thought of letting the nodes move along a path (e.g. a small circle), each one at its individual speed.

Is there a way to do this by using SVG/CSS only? Or do I need to look for a solution with js or snap? Since the menu is a core-functional part of the webpage, I want to keep it as simple and vanilla as possible.

Here's a working example with two polygons, which I copied from this thread.

polygon {
  stroke-width: 1;
  stroke: red;
  fill: transparent;
}
polygon:hover {
  fill: red;
}
<svg viewBox="0 0 999 799">
  <polygon points="445,161 345,174 500,10" />

  <polygon points="445,161 345,174 500,270" />
</svg>

The following snippet shows the only way I could come up with so far to move one single node. Unfortunately, it requires the coordinates of all the other points. Since the nodes are shared between the various polygons this solution does not work for me.

<polygon points="" fill="red">
 <animate attributeName="points" dur="1s" fill="freeze"
          from="0,0, 0,100, 100,100"
          to="0,0, 0,100, 100,50"/>
</polygon>

Thank you very much for your help!


Solution

  • I would use javascript for this and I would use an array of points. Then I would animate the position of those points. You draw the polygons using those points. See function function resetPoints() in my code. I hope this is what you are asking for.

    let points = [
      {x:445,y:161,a:.7},
      {x:345,y:147,a:2.1},
      {x:500,y:10,a:3.9},
      {x:500,y:270,a:5.2}
    ]
    
    let r = 20;// the radius of rotation. In this case is a unique value. It may be a different value for every point
    let speed = .01;//the speed of rotation. In this case is a unique value. It may be a different value for every point 
    
    
    //get center rotation
    points.forEach(p=>{
      p.c = {};
      let a = 2*Math.PI - p.a;//angle
      p.c.x = p.x + r*Math.cos(a);
      p.c.y = p.y + r*Math.sin(a);
    });
    
    
    
    //resetPoints();
    
    function Frame(){
      requestAnimationFrame(Frame)
      points.forEach(p=>{
        p.a += speed;
        p.x = p.c.x + r*Math.cos(p.a);
        p.y = p.c.y + r*Math.sin(p.a);
      });
      
      resetPoints();
    }
    
    Frame();
    
    // a function to draw the polygons in base of the points
    
    function resetPoints(){
      let pts1 = `${points[0].x}, ${points[0].y} 
                ${points[1].x}, ${points[1].y} 
                ${points[2].x}, ${points[2].y}`
      let pts2 = `${points[0].x}, ${points[0].y} 
                ${points[1].x}, ${points[1].y} 
                ${points[3].x}, ${points[3].y}`
    
    a.setAttributeNS(null,"points",pts1);
    b.setAttributeNS(null,"points",pts2);
    }
    polygon {
      stroke-width: 1;
      stroke: red;
      fill: transparent;
    }
    polygon:hover {
      fill: red;
    }
    <svg viewBox="0 0 999 799">
      <polygon id="a" points="445,161 345,174 500,10" />
    
      <polygon id="b" points="445,161 345,174 500,270" />
    </svg>