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

时间:2011-04-20 20:44:31

标签: svg

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

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

更一般地说,用

表示圆的一部分弧的路径是什么
x, y, r, d1, d2, direction

含义

center (x,y), radius r, degree_start, degree_end, direction

14 个答案:

答案 0 :(得分:319)

扩展@ 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;       
}

使用

document.getElementById("arc1").setAttribute("d", describeArc(200, 400, 100, 0, 180));

并在你的HTML中

<path id="arc1" fill="none" stroke="#446688" stroke-width="20" />

Live demo

答案 1 :(得分:121)

您想使用elliptical Arc command。不幸的是,这需要你指定起点和终点的笛卡尔坐标(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];
}

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

arc命令具有以下参数:

rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y

第一个例子:

rx = ry = 25且x-axis-rotation = 0,因为您需要一个圆而不是一个椭圆。您可以使用上面的函数计算起始坐标(您应该M ove到)和结束坐标(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

对于第二个例子(假设你指的是同一个方向,因而是一个大弧),SVG路径是:

M 200 175 A 25 25 0 1 0 217.678 217.678

同样,我还没有测试过这些。

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

  

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

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

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

答案 2 :(得分: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));

HTML

<svg>
  <path id="arc1" fill="orange" stroke="#446688" stroke-width="0" />
</svg>

答案 3 :(得分:4)

这是一个老问题,但我发现代码很有用并且让我三分钟思考:)所以我在 @opsb 的答案中添加了一个小扩展。

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

&#13;
&#13;
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)
&#13;
<p id="p">
</p>
<svg width="300" height="300" style="border:1px gray solid">
  <path id="path" fill="blue" stroke="cyan"></path>
</svg>
&#13;
&#13;
&#13;

然后你去!

答案 4 :(得分:3)

我想评论一下@Ahtenus的答案,特别是关于Ray Hulha的评论说,codepen没有显示任何弧形,但我的声誉不够高。

这个codepen无法正常工作的原因是它的html错误,行程宽度为零。

我修复了它并在此处添加了第二个示例:http://codepen.io/AnotherLinuxUser/pen/QEJmkN

html:

<svg>
    <path id="theSvgArc"/>
    <path id="theSvgArc2"/>
</svg>

相关的CSS:

svg {
    width  : 500px;
    height : 500px;
}

path {
    stroke-width : 5;
    stroke       : lime;
    fill         : #151515;
}

javascript:

document.getElementById("theSvgArc").setAttribute("d", describeArc(150, 150, 100, 0, 180));
document.getElementById("theSvgArc2").setAttribute("d", describeArc(300, 150, 100, 45, 190));

答案 5 :(得分:3)

@ opsb的答案很整洁,但中心点不准确,而且,正如@Jithin所说,如果角度是360,那么根本就没有绘制。

@Jithin解决了360问题,但是如果你选择的程度低于360度,那么你将得到一条关闭弧形循环的线,这不是必需的。

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

&#13;
&#13;
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);
    
&#13;
<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>
&#13;
&#13;
&#13;

答案 6 :(得分:3)

图像和一些Python

只是为了更好地澄清并提供另一种解决方案。 Arc [A]命令使用当前位置作为起点,因此您必须先使用Moveto [M]命令。

然后Arc的参数如下:

rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, xf, yf

如果我们定义以下svg文件:

<svg width="5cm" height="5cm" viewBox="0 0 500 500"

xmlns="http://www.w3.org/2000/svg" version="1.1">

以下代码将为您提供此结果:

<g stroke="none" fill="lime">
<path d="
M 250 250
A 100 100 0 0 0 450 250
Z"/> 
</g>

enter image description here

您将使用M的{​​{1}}和xf的{​​{1}}参数设置起点yf

我们正在寻找圈子,所以我们设置A等于rx这样做基本上现在它会尝试找到与起点和终点相交的半径ry的所有圆圈。

rx

您可以在我撰写的this post中获得更详细的解释。

答案 7 :(得分:2)

略微修改@ opsb的回答。我们不能用这种方法绘制一个完整的圆圈。即如果我们给(0,360)它将不会绘制任何东西。所以稍作修改就可以解决这个问题。显示有时达到100%的分数可能很有用。

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 endAngleOriginal = endAngle;
    if(endAngleOriginal - startAngle === 360){
        endAngle = 359;
    }

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

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

    if(endAngleOriginal - startAngle === 360){
        var d = [
              "M", start.x, start.y, 
              "A", radius, radius, 0, arcSweep, 0, end.x, end.y, "z"
        ].join(" ");
    }
    else{
      var d = [
          "M", start.x, start.y, 
          "A", radius, radius, 0, arcSweep, 0, end.x, end.y
      ].join(" ");
    }

    return d;       
}

document.getElementById("arc1").setAttribute("d", describeArc(120, 120, 100, 0, 359));

答案 8 :(得分:2)

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,
    ].join(' ');

    if (fullCircle) d.push('z');
    return d;
};

答案 9 :(得分:1)

wdebeaum的原始polarToCartesian函数是正确的:

var angleInRadians = angleInDegrees * Math.PI / 180.0;

使用以下方式反转起点和终点

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

令我感到困惑(对我而言),因为这会扭转扫描标志。使用:

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

使用sweep-flag =&#34; 0&#34;画&#34;正常&#34;反时钟弧, 我认为更直接。 见https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths

答案 10 :(得分:1)

给求职者(我也是)的注意事项-如果不是必须使用弧线,绘制半圆的简单得多的解决方案是使用SVG的stroke-dasharray <circle>

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

看不到一个余弦。

完整示例,并附有说明: https://codepen.io/mjurczyk/pen/wvBKOvP

答案 11 :(得分:0)

你可以使用我为上面的答案制作的JSFiddle代码:

https://jsfiddle.net/tyw6nfee/

您需要做的就是更改最后一行console.log代码并为其指定自己的参数:

  console.log(describeArc(255,255,220,30,180));
  console.log(describeArc(CenterX,CenterY,Radius,startAngle,EndAngle))

答案 12 :(得分:0)

// putr following above class
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;       
}

// put following in render
 <Svg width={150} height={150}>
            <Text style={{position:'absolute', top:50,left:60}}>24</Text> 
            <Text style={{position:'absolute', top:70,left:50}}>Rooms</Text> 
  <Path
d={showArc}
fill="transparent"
stroke="green"
strokeWidth="15"
/>
 <Path
d={showArc1}
fill="transparent"
stroke={Colors.lightOrange}
strokeWidth="15"
/>
<Path
d={showArc2}
fill="transparent"
stroke="gray"
strokeWidth="15"
/> 
</Svg>

答案 13 :(得分:-1)

基于所选答案的ReactJS组件:

import React from 'react';

const 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))
    };
};

const describeSlice = (x, y, radius, startAngle, endAngle) => {

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

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

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

    return d;
};

const path = (degrees = 90, radius = 10) => {
    return describeSlice(0, 0, radius, 0, degrees) + 'Z';
};

export const Arc = (props) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300">
    <g transform="translate(150,150)" stroke="#000" strokeWidth="2">
        <path d={path(props.degrees, props.radius)} fill="#333"/>
    </g>

</svg>;

export default Arc;