You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SVG半圆弧纯CSS/JS动画调试求助:解决180度顺时针动画收缩问题并实现双圆弧同步动画

Fixing SVG Arc Animation Shrinkage & Implementing Dual-Direction Sweeps

Hey there, let's get that arc animation working properly! The weird shrinking you're seeing is caused by SVG shape interpolation rules—when animating between two paths, their command sequences and parameters have to be exactly matched (same command types, same number of parameters, consistent arc flags). If they don't line up, the browser can't smoothly interpolate the shapes, leading to odd distortions.

Here's how to fix this and implement your desired dual-animation (top half clockwise, bottom half counter-clockwise):


1. First: Align Path Structures for Valid Interpolation

Your original start and end paths had mismatched arc parameters and coordinates. To fix this, we need to ensure:

  • Both the start and end paths use the exact same command sequence (M → A → L → L)
  • All arc parameters (radius, rotation, large-arc flag, sweep flag) stay identical between start/end states—only the arc's end point changes. This tells the browser to only interpolate the position, not the arc's basic shape.

2. Full Working Code

I've adjusted the radius to fit your SVG viewbox (no more overflow!) and set up synchronized animations for both halves:

<svg viewBox="0 0 300 300" height="250" width="250" style="background-color: black;">
  <!-- Base white circle (visible as arcs are swept away) -->
  <path fill="white" stroke-width="0" d="M 300 150 A 150 150 0 0 0 0 150 L 150 150 L 300 150" />
  
  <!-- Top half: clockwise sweep animation (black cover fades out) -->
  <path class="arc-top" fill="black" stroke-width="0" 
        d="M 300 150 A 150 150 0 0 0 150 0 L 150 150 L 300 150" />
  
  <!-- Bottom half: counter-clockwise sweep animation (black cover fades out) -->
  <path class="arc-bottom" fill="black" stroke-width="0" 
        d="M 300 150 A 150 150 0 0 1 150 300 L 150 150 L 300 150" />
</svg>

<style>
.arc-top {
  animation: sweep-top 5s linear forwards;
}

.arc-bottom {
  animation: sweep-bottom 5s linear forwards;
}

/* Top arc: sweep clockwise from full half-circle to a point */
@keyframes sweep-top {
  to {
    d: path("M 300 150 A 150 150 0 0 0 300 150 L 150 150 L 300 150");
  }
}

/* Bottom arc: sweep counter-clockwise from full half-circle to a point */
@keyframes sweep-bottom {
  to {
    d: path("M 300 150 A 150 150 0 0 1 300 150 L 150 150 L 300 150");
  }
}
</style>

<script>
// Keep your utility functions for dynamic path generation if needed
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;
}
</script>

Key Details Explained

  • Viewbox & Radius Match: I set the radius to 150 to perfectly fit your 300x300 viewBox, so no more overflow issues.
  • Sweep Flag Control: The arc's 5th parameter (sweep flag) determines direction: 0 for clockwise (top half), 1 for counter-clockwise (bottom half). This is how we get opposite directions.
  • Consistent Path Structure: The start and end paths for each animation are identical except for the arc's end coordinate. This ensures smooth interpolation without shape distortion.
  • Synchronized Animations: Both animations use the same duration (5s) and timing function (linear), with forwards to keep the final state—so they play in perfect sync.

Bonus: Dynamic Path Generation

If you want to generate paths dynamically later, use your describeArc function to create both start and end paths. Just make sure the start/end pairs have identical command structures. For example:

// Generate start path for top arc (180-degree clockwise arc)
const topStart = describeArc(150, 150, 150, 0, 180);
// Generate end path for top arc (0-degree arc, i.e., a point)
const topEnd = describeArc(150, 150, 150, 0, 0);

This guarantees the paths are compatible for smooth animation.

内容的提问来源于stack exchange,提问作者Paulo Filho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 21:19:04