javascriptanimationsvganimatetransform

Smoothly move an SVG line along a rectangular path


I am trying to recreate the following animation:

enter image description here

I am having trouble with getting a line to properly animate around corners. I've tried using an animateMotion-element like so:

<line class="testLine" x1="10" y1="10" x2="100" y2="10" stroke="white"> 
        <animateMotion dur="1.6s" repeatCount="indefinite" 
            path="M 10 10 L 390 10
             M 390 10 L 390 290
             M 390 290 L 10 290
             M 10 290 L 10 10"> 
        </animateMotion>
</line> 

But the line is not smoothly rounding corners. Any idea on how to get it to make it move smoothly around corners as shown in the GIF?


Solution

  • You will need to create a path with srtoke-dasharray = 1/2 of the side of the rect and animate the stroke-dashoffset of the path to 0

    Please read the comments in the code:

    const SVG_NS = "http://www.w3.org/2000/svg";
    let sz = 50;//initial size 1/2 rect side
    //the array of the paths. Inside the array uou have a first object for the path #p
    let sqRy = [{ s: sz, d: "", l: p.getTotalLength(), el: p }];
    
    //create several paths and rotate those paths accordingly
    for (let i = 1; i < 8; i++) {
      let o = {};
      let size = sqRy[i - 1].s / 2;
      
      o.s = Math.sqrt(2 * size * size);
      //the value od the d attribute of the new path
      o.d = `M-${o.s},-${o.s}L${o.s},-${o.s} ${o.s},${o.s} -${o.s},${o.s} -${o.s},-${o.s}z`;
    
      o.el = document.createElementNS(SVG_NS, "path");
      o.el.setAttribute("d", o.d);//set the d attribute
      o.el.setAttribute("transform", `rotate(${45 * i})`);//set the rotation
      svg.appendChild(o.el);//append the new path to the svg element
      o.l = o.el.getTotalLength();//calculate the total length of the new path
      //push the object for the path to the array
      sqRy.push(o);
    }
    
    
    //for every element in the array set the stroke-dasharray and the stroke-dashoffset.
    sqRy.map((sq) => {
      sq.el.setAttribute("style", `stroke-dasharray:${sq.s};stroke-dashoffset:${sq.l}`);
    });
    svg{fill:none;stroke:black;}
    
    path{
         animation: dash 16s linear infinite;
    }
    
    @keyframes dash {
      to {
        stroke-dashoffset: 0;
      }
    }
    <svg width="300" viewBox="-60 -60 120 120" id="svg">
      <path id="p" d="M-50,-50L50,-50 50,50 -50,50 -50,-50z" />  
    </svg>