沿矩形路径平滑移动 SVG 线

Smoothly move an SVG line along a rectangular path

我正在尝试重新创建以下动画:

我无法让一条线在拐角处正确设置动画。我试过像这样使用 animateMotion 元素:

<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> 

但是线条转角不流畅。关于如何让它顺利地绕过角落移动,如 GIF 中所示,有什么想法吗?

您需要创建一个路径,其中 srtoke-dasharray = 矩形边的 1/2,并将路径的 stroke-dashoffset 动画化为 0

请阅读代码中的注释:

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>

同样的事情只是缩放旋转和平方的对角线乘数 - sqrt(2)

document.querySelector('svg').innerHTML = [...Array(11)]
  .map((_, i) => Math.pow(2, i/2))
  .map((s, i) => `
    <path transform="scale(${s})rotate(${i*45})" 
          stroke-width="${2/s}" 
          d="m5,5h-10v-10h10z"></path>
`).join(',');
path {
  stroke-dasharray: 5 5;
  fill: none;
  stroke: black;
  animation: shift 3s infinite linear;
}

@keyframes shift {
  100% {stroke-dashoffset: -20}
}
<svg viewBox='-200,-200,400,400' width="90vw" height="90vh"></svg>

不是计算出来的,而是通过svg属性变换对齐svg矩形(首选svg路径)并利用css stroke-dasharray 和 stroke-offset 进行动画处理,可以获得结果

if js is not an option then you can just add and align the rectangles you need

代码

   svg>rect {
            stroke-dasharray: 100;
            -webkit-animation: draw 5s infinite linear;
            /* Chrome, Safari, Opera */
            animation: draw 5s infinite linear;
        }
        
        @keyframes draw {
            to {
                stroke-dashoffset: 1000;
            }
        }
        
        @-moz-keyframes draw {
            to {
                stroke-dashoffset: 1000;
            }
        }
        
        @-webkit-keyframes draw {
            to {
                stroke-dashoffset: 1000;
            }
        }
<svg width="500" height="500">
    <rect width="300" height="300" style="fill:none;stroke-width:2;stroke:black" />
    <rect width="210" height="210" transform='translate(150 1) rotate(45)' style="fill:none;stroke-width:2;stroke:black" />
    <rect width="150" height="150" transform='translate(75 75)' style="fill:none;stroke-width:2;stroke:black" />
  </svg>