如何计算弧(圆)的SVGpath
给定以(200,200),半径25为中心的圆,我如何绘制270度到135度的弧线和270度到45度的弧线?
0度表示在x轴上(右侧)(意思是3点钟位置),270度表示12点钟位置,90表示6点钟位置
更一般地说,圆的一部分是什么是一个弧的path
x, y, r, d1, d2, direction
含义
center (x,y), radius r, degree_start, degree_end, direction
在@ wdebeaum的最佳答案上展开,下面是一个生成弧形path的方法:
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" />
现场演示
你想要使用椭圆A
rc命令 。 不幸的是,这需要你指定开始点和结束点的笛卡尔坐标(x,y),而不是你所拥有的极坐标(半径,angular度),所以你必须做一些math计算。 这里有一个JavaScript函数可以工作(尽pipe我没有testing过),我希望这个函数是不言而喻的:
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]; }
哪个angular度对应于哪个时钟位置取决于坐标系统; 只是在必要时交换和/或否定sin / cos项。
arc命令有这些参数:
rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y
举个例子:
rx
= ry
= 25和x-axis-rotation
= 0,因为你想要一个圆而不是一个椭圆。 你可以使用上面的函数计算起始坐标(你应该到的)和结束坐标(x,y),分别产生(200,175)和大约(182.322,217.678)。 鉴于迄今为止的这些限制,实际上有四个弧可以被绘制,所以两个标志select其中之一。 我猜你可能想要在减小angular度(意思是sweep-flag
= 0)的方向上绘制一个小圆弧(意思是large-arc-flag
sweep-flag
= 0)。 总之,SVG的path是:
M 200 175 A 25 25 0 0 0 182.322 217.678
对于第二个例子(假设你的意思是相同的方向,因此一个大弧),SVGpath是:
M 200 175 A 25 25 0 1 0 217.678 217.678
再次,我没有testing这些。
(编辑2016-06-01)如果像@clocksmith一样,你想知道他们为什么select这个API,那么看一下实现注释 。 他们描述了两个可能的电弧参数化,“端点参数化”(他们select的)和“中心参数化”(就像问题所使用的那样)。 在“端点参数化”的描述中,他们说:
端点参数化的优点之一是它允许一致的path语法,其中所有的path命令都以新的“当前点”的坐标结束。
所以基本上这是一个弧的副作用被认为是一个更大的path的一部分,而不是他们自己的单独的对象。 我想如果你的SVG渲染器是不完整的,只要知道它有多less个参数,就可以跳过任何不知道如何渲染的path组件。 或者,也许它可以并行渲染具有许多组件的path的不同块。 或者也许他们这样做是为了确保四舍五入的错误不会沿着复杂的path延伸。
实现注释对于原始问题也是有用的,因为它们在两个参数化之间有更多的math伪代码(当我第一次写这个答案时我没有意识到)。
我略微修改了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>
我想评论一下@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));
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 =“0”绘制“正常的”反时钟方向的弧线,我认为这是更直接的。 见https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
对@ 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));
@ opsb的答案是整齐的,但中心点是不准确的,此外,正如@Jithin指出的,如果angular度是360,那么什么都不是。
@Jithin修正了360问题,但是如果你select了小于360度,那么你会得到一个closures弧线的线,这是不需要的。
我修正了这个问题,并在下面的代码中添加了一些animation:
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);
<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>
这是一个古老的问题,但我发现代码有用,并保存了我三分钟的思维:)所以我给@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)
<p id="p"> </p> <svg width="300" height="300" style="border:1px gray solid"> <path id="path" fill="blue" stroke="cyan"></path> </svg>
基于所选答案的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;
你可以使用JSFiddle代码,我为上面的答案:
https://jsfiddle.net/tyw6nfee/
所有你需要做的是改变最后一行console.log代码,并给它你自己的参数:
console.log(describeArc(255,255,220,30,180)); console.log(describeArc(CenterX,CenterY,Radius,startAngle,EndAngle))