SVG半弧顺时针动画超180度(半个圆)纯CSS/JS

SVG semi-arc clockwise animation over 180 degrees (half of a circle) with pure CSS/JS

我的代码需要一些帮助。我的目标是制作一个 180 度渐进式动画,该动画在圆的上半部分顺时针旋转。我的最终目标是同时播放两个动画:上半部分顺时针,下半部分逆时针,两者的方式完全相同。

到目前为止我有这个:

// Circle functions
  function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
    const angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;

    return {
      x: centerX + (radius * Math.cos(angleInRadians)),
      y: centerY + (radius * Math.sin(angleInRadians))
    };
  }

  function describeArc(x, y, radius, startAngle, endAngle){
    const start = polarToCartesian(x, y, radius, endAngle);
    const end = polarToCartesian(x, y, radius, startAngle);

    const arcSweep = endAngle - startAngle <= 180 ? "0" : "1";

    const d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, arcSweep, 0, end.x, end.y,
        "L", x,y,
        "L", start.x, start.y
    ].join(" ");

    return d;       
  }
.arc {
  animation: moveForward 5s linear forwards;
}

@keyframes moveForward {
  to {
    d: path("M 450 150 A 300 300 0 0 0 450 150 L 150 150 L 450 150");
  }
}
<svg viewBox="0 0 300 300" height="250" width="250" style="background-color: black;">
  <path fill="white" stroke-width="0" d="M 450 150 A 300 300 0 0 0 -150 149.99999999999997 L 150 150 L 450 150" />
  <path class="arc" fill="black" stroke-width="0" d="M 450 150 A 300 300 0 0 0 -145.44232590366238 97.90554669992092 L 150 150 L 450 150" />
</svg>

不介意外推半径,我想为这个动画吐出SVG区域内的动画。如您所见,它沿着路径前进但缺少某些东西,因为它在动画过程中开始缩小。我不是专家,但形状插值可能缺少某些东西。

我正在使用公式根据另一个有点相关的问题 this answer 生成终点。它基本上将我的弧角转换为笛卡尔坐标。

感谢任何意见,非常感谢。

一个可能的解决方案是为非常粗的笔画(圆半径的两倍)设置动画。

在这种情况下,圆的半径是 20,所以 stroke-width="40"

在下一个示例中,我正在为路径的 stroke-dasharray 设置动画:62.84,0(行程长度 = 62.84,间隙 = 0)到 0、62.4(行程长度 = 0,间隙 = 62.84 ) 其中 62.84 是路径的长度。

请阅读如何 How SVG Line Animation Works

path {
  stroke-dasharray: 62.84,0;
  animation: anim 5s linear infinite;
}

@keyframes anim {
  to {
    stroke-dasharray: 0, 62.84;
  }
}
<svg viewBox="-50 -50 100 100" width="90vh">
  <path stroke-width="40" fill="none" stroke="black" d="M20,0A20,20 0 0 0 -20,0"/> 
</svg>

如您所见,我只使用了 css。您可能需要使用 JS 来计算路径的长度。您可以使用 getTotalLength() 方法来完成。

更新

OP 正在评论:

if I were to expand the radius so it covers the SVG area (the square container) entirely, without any padding, what's the factor between this enlarged radius and your solution? What should I take into account?

在这种情况下,可见圆(半径 + stroke-width/2)应至少为 70.71,但也可以大于此值。 70.71 表示中心和 svg 元素的一个角之间的距离。这是双边 = 50 的 right-angled 三角形的斜边。由于 viewBox="-50 -50 100 100" 边 = 100/2。

这意味着 stroke-width="70.71" 并且您需要像这样重写 d 属性:

d="M35.355,0A35.355,35.355 0 0 0 -35.355,0"

其中 35.355 = 70.71/2

还需要重新计算路径的长度(我使用的是 p.getTotalLength() 方法),在本例中为 111.09。我将此值用于 CSS 中的 stroke-dasharray。

console.log(p.getTotalLength())
svg{border:solid}

path {
  stroke-dasharray: 111.09,0;
  animation: anim 5s linear infinite;
}

@keyframes anim {
  to {
    stroke-dasharray: 0, 111.09;
  }
}
<svg viewBox="-50 -50 100 100" width="90vh">
  <path id="p" stroke-width="70.71" fill="none" stroke="black" 
  d="M35.355,0A35.355,35.355 0 0 0 -35.355,0"/> 
</svg>

根据评论; Enxaneta 她的回答,没有计算 pathLength,而是设置 pathLength="1"

svg {
  border: solid
}

path {
  stroke-dasharray: 1, 0;
  animation: anim 5s linear infinite;
}

@keyframes anim {
  to {
    stroke-dasharray: 0, 1.1;
  }
}
<svg viewBox="-50 -50 100 100" height="180px">
  <path pathLength="1" 
        stroke-width="70.71" fill="none" stroke="black" 
        d="M35.355,0A35.355,35.355 0 0 0 -35.355,0"/> 
</svg>