如何计算圆弧(圆弧)的SVG路径

nop*_*ole 179 svg

给定一个以(200,200),半径25为中心的圆,如何绘制从270度到135度的弧和一个从270度到45度的弧?

0度表示它在x轴(右侧)上(意味着它是3点钟位置)270度表示它是12点钟位置,90表示它是6点钟位置

更一般地说,圆弧的一部分的圆弧路径是什么

x, y, r, d1, d2, direction
Run Code Online (Sandbox Code Playgroud)

含义

center (x,y), radius r, degree_start, degree_end, direction
Run Code Online (Sandbox Code Playgroud)

ops*_*psb 345

扩展@ wdebeaum的好答案,这是一个生成弧形路径的方法:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var 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){

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

    var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, largeArcFlag, 0, end.x, end.y
    ].join(" ");

    return d;       
}
Run Code Online (Sandbox Code Playgroud)

使用

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 180));
Run Code Online (Sandbox Code Playgroud)

并在你的HTML中

<path id="arc1" fill="none" stroke="#446688" stroke-width="20" />
Run Code Online (Sandbox Code Playgroud)

现场演示

  • 这超级棒!请注意,`arcSweep`变量实际上是控制`large-arc-flag` svg A参数.在上面的代码中,`sweep-flag`参数的值始终为零.`arcSweep`应该可以重命名为`longArc`. (8认同)
  • 并且不要忘记切割少量的弧长,如:`endAngle - 0.0001`,如果没有,则不会渲染完整的弧. (3认同)
  • 非常有帮助,谢谢.我发现唯一的事情是如果你使用负角度,largeArc逻辑不起作用.这适用于-360到+360:http://jsbin.com/kopisonewi/2/edit?html,js,output (2认同)

wde*_*aum 125

您想使用椭圆Arc命令.不幸的是,这需要你指定起点和终点的笛卡尔坐标(x,y)而不是你拥有的极坐标(半径,角度),所以你必须做一些数学运算.这是一个应该工作的JavaScript函数(虽然我还没有测试过),我希望它是相当不言自明的:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var angleInRadians = angleInDegrees * Math.PI / 180.0;
  var x = centerX + radius * Math.cos(angleInRadians);
  var y = centerY + radius * Math.sin(angleInRadians);
  return [x,y];
}
Run Code Online (Sandbox Code Playgroud)

哪个角度对应于哪个时钟位置将取决于坐标系; 只需交换和/或否定sin/cos术语.

arc命令具有以下参数:

rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y
Run Code Online (Sandbox Code Playgroud)

第一个例子:

rx= ry= 25且x-axis-rotation= 0,因为你想要一个圆而不是一个椭圆.您可以使用上面的函数分别计算起始坐标(您应该过去M)和结束坐标(x,y),分别得出(200,175)和约(182.322,217.678).到目前为止,鉴于这些限制,实际上可以绘制四个弧,因此两个标志选择其中一个.我猜你可能想要large-arc-flag在减小角度的方向上绘制一个小弧(意思是= 0)(意思是sweep-flag= 0).总之,SVG路径是:

M 200 175 A 25 25 0 0 0 182.322 217.678
Run Code Online (Sandbox Code Playgroud)

对于第二个示例(假设您指的是相同的方向,因此是一个大弧),SVG路径是:

M 200 175 A 25 25 0 1 0 217.678 217.678
Run Code Online (Sandbox Code Playgroud)

我再次测试过这些.

(编辑2016-06-01)如果像@clocksmith一样,您想知道他们为什么选择这个API,请查看实施说明.他们描述了两种可能的弧参数化,"端点参数化"(他们选择的那种)和"中心参数化"(就像问题所用的那样).在"端点参数化"的描述中,他们说:

端点参数化的一个优点是它允许一致的路径语法,其中所有路径命令都以新的"当前点"的坐标结束.

所以基本上它是弧的副作用被视为更大路径的一部分而不是它们自己的独立对象.我想如果你的SVG渲染器不完整,它可以跳过它不知道如何渲染的任何路径组件,只要它知道它们采用了多少个参数.或者它可以实现具有许多组件的路径的不同块的并行渲染.或许他们这样做是为了确保舍入错误不会沿着复杂路径的长度积累.

实现说明对于原始问题也很有用,因为它们有更多的数学伪代码用于在两个参数化之间进行转换(我在第一次写这个答案时没有意识到).


Mac*_*zyk 17

如果不必使用圆弧,则绘制部分圆的更简单的解决方案是使用stroke-dasharraySVG <circle>

将破折号数组分为两个元素,并将它们的范围缩放到所需的角度。可以使用 调整起始角度stroke-dashoffset

看不到任何余弦。

带有解释的完整示例: https ://codepen.io/mjurczyk/pen/wvBKOvP

// Simpler alternative to using SVG arcs: http://xahlee.info/js/svg_circle_arc.html
// More about dash arrays: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
// Setup DOM elements.
const input = document.querySelector('input');
const circle = document.querySelector('circle');

// All calculations are within 'run' function.
const run = () => {
  // 1. Get angle from input field.
  let angle = parseFloat(input.value) || 0;

  // 2. Radius of SVG circle.
  const radius = 50;
  const circumference = 2 * Math.PI * radius;

  // 3. First, 1/4 of circumfence of 90 degrees. To start from top of the view,
  //    we must rotate it by 90 degrees. By default circle will start on the right.
  //    Stroke offset effectively rotates the circle.
  // 4. Second, calculate dash array. We need dash array containing only two parts -
  //    visible dash, and invisible dash.
  //    Visible dash should have length of the chosen angle. Full circle is 360 degrees,
  //    and this 360 degrees does also equal the entire circumference. We want just a part of
  //    this entire circle to be visible - (angle / 360 degrees) returns a percentage value
  //    (between 0.0 and 1.0) of how much circumference should be visible.
  //    Hence, we then multiply (angle / 360) times the entire circumference.
  const strokeOffset = (1 / 4) * circumference;
  const strokeDasharray = (angle / 360) * circumference;

  // 5. Set circle radius
  circle.setAttribute('r', 50);
  // 6. Create dash array of two elements (combined they must equal the entire circumference).
  //    First has the length of visible portion. Second, the remaining part.
  circle.setAttribute('stroke-dasharray', [
    strokeDasharray,
    circumference - strokeDasharray
  ]);
  // 7. (Optional) Rotate circle to start from the top.
  circle.setAttribute('stroke-dashoffset', strokeOffset);
}

// Run and update DOM
input.addEventListener('keyup', run);
run();
Run Code Online (Sandbox Code Playgroud)
/* You can ignore this part, too */

svg,
input {
  display: block;
  margin: 2px;
}

svg {
  width: 200px;
  height: 200px;
  stroke: #000;
  stroke-width: 2px;
  fill: transparent;
}
Run Code Online (Sandbox Code Playgroud)
<!-- You can ignore this part -->
<input type="number" placeholder="angle (deg)" value="90" />
<svg>
  <circle cx="100" cy="100" r="0" />
</svg>
Run Code Online (Sandbox Code Playgroud)


小智 16

我略微修改了opsb的答案,并支持填充圆圈扇区. http://codepen.io/anon/pen/AkoGx

JS

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
  var 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){

    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);

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

    var 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;       
}

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 220));
Run Code Online (Sandbox Code Playgroud)

HTML

<svg>
  <path id="arc1" fill="orange" stroke="#446688" stroke-width="0" />
</svg>
Run Code Online (Sandbox Code Playgroud)

  • codepen链接似乎不适合我(Chrome) (5认同)

Has*_*sef 10

@opsb 的答案很简洁,但中心点不准确,而且,正如@Jithin 指出的那样,如果角度是 360,则根本不会绘制任何内容。

@Jithin 修复了 360 度问题,但如果您选择的角度小于 360 度,那么您将得到一条关闭圆弧环的线,这不是必需的。

我修复了这个问题,并在下面的代码中添加了一些动画:

function myArc(cx, cy, radius, max){       
       var circle = document.getElementById("arc");
        var e = circle.getAttribute("d");
        var d = " M "+ (cx + radius) + " " + cy;
        var angle=0;
        window.timer = window.setInterval(
        function() {
            var radians= angle * (Math.PI / 180);  // convert degree to radians
            var x = cx + Math.cos(radians) * radius;  
            var y = cy + Math.sin(radians) * radius;
           
            d += " L "+x + " " + y;
            circle.setAttribute("d", d)
            if(angle==max)window.clearInterval(window.timer);
            angle++;
        }
      ,5)
 }     

  myArc(110, 110, 100, 360);
    
Run Code Online (Sandbox Code Playgroud)
<svg xmlns="http://www.w3.org/2000/svg" style="width:220; height:220;"> 
    <path d="" id="arc" fill="none" stroke="red" stroke-width="2" />
</svg>
Run Code Online (Sandbox Code Playgroud)


MrE*_*MrE 6

ES6版本:

const angleInRadians = angleInDegrees => (angleInDegrees - 90) * (Math.PI / 180.0);

const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
    const a = angleInRadians(angleInDegrees);
    return {
        x: centerX + (radius * Math.cos(a)),
        y: centerY + (radius * Math.sin(a)),
    };
};

const arc = (x, y, radius, startAngle, endAngle) => {
    const fullCircle = endAngle - startAngle === 360;
    const start = polarToCartesian(x, y, radius, endAngle - 0.01);
    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,
        fullCircle ? 'Z':''
    ].join(' ');

    return d;
};
Run Code Online (Sandbox Code Playgroud)

  • 您可以通过利用 ES6 模板文字使示例更加清晰: `const d = \`M ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArc} 0 ${end .x} ${end.y}\`` (5认同)

Sum*_*ron 5

这是一个老问题,但是我发现代码很有用,并节省了三分钟的时间:)因此,我在@opsb的答案中添加了一个小扩展。

如果您想将此弧线转换为切片(以允许填充),我们可以稍作修改代码:

function describeArc(x, y, radius, spread, startAngle, endAngle){
    var innerStart = polarToCartesian(x, y, radius, endAngle);
  	var innerEnd = polarToCartesian(x, y, radius, startAngle);
    var outerStart = polarToCartesian(x, y, radius + spread, endAngle);
    var outerEnd = polarToCartesian(x, y, radius + spread, startAngle);

    var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";

    var d = [
        "M", outerStart.x, outerStart.y,
        "A", radius + spread, radius + spread, 0, largeArcFlag, 0, outerEnd.x, outerEnd.y,
        "L", innerEnd.x, innerEnd.y, 
        "A", radius, radius, 0, largeArcFlag, 1, innerStart.x, innerStart.y, 
        "L", outerStart.x, outerStart.y, "Z"
    ].join(" ");

    return d;
}

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

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

var path = describeArc(150, 150, 50, 30, 0, 50)
document.getElementById("p").innerHTML = path
document.getElementById("path").setAttribute('d',path)
Run Code Online (Sandbox Code Playgroud)
<p id="p">
</p>
<svg width="300" height="300" style="border:1px gray solid">
  <path id="path" fill="blue" stroke="cyan"></path>
</svg>
Run Code Online (Sandbox Code Playgroud)

然后你去!