给定一个以 (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
扩展@wdebeaum's great answer,这是一种生成弧形路径的方法:
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" />
您想使用 elliptical A
rc 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
转向)和结束坐标 (x, y),分别得出 (200, 175) 和大约 (182.322, 217.678)。到目前为止,鉴于这些约束,实际上可以绘制四个弧,因此两个标志选择其中一个。我猜你可能想在角度减小的方向(意思是 sweep-flag
=0)画一个小弧(意思是 large-arc-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 渲染器不完整,它可以跳过任何它不知道如何渲染的路径组件,只要它知道它们需要多少参数。或者它可以并行渲染具有许多组件的路径的不同块。或者也许他们这样做是为了确保舍入误差不会沿着复杂路径的长度累积。
实现说明对原始问题也很有用,因为它们有更多的数学伪代码用于在两个参数化之间进行转换(当我第一次写这个答案时我没有意识到)。
large-arc-flag
必须从 0 切换到 1 的事实可能会引入一些错误。
我稍微修改了 the answer of 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>
centerX
,centerY
更改为 100。
viewBox="0 0 500 500"
这是一个老问题,但我发现代码很有用,并为我节省了三分钟的思考时间 :) 所以我在 @opsb's answer 中添加了一个小扩展。
如果您想将此弧转换为切片(以允许填充),我们可以稍微修改代码:
function describeArc(x, y, radius, spread, startAngle, endAngle){ var innerStart = polarToCartesian(x, y, radius, endAngle); var innerEnd = polarToCartesian(x, y, 半径, 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(" ");返回 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)
你去吧!
对于寻求答案的人(我也是)的注意事项 - 如果不是必须使用圆弧,绘制部分圆的更简单的解决方案是使用 SVG <circle>
的 stroke-dasharray
。
将破折号数组分成两个元素,并将它们的范围缩放到所需的角度。可以使用 stroke-dashoffset
调整起始角度。
看不到一个余弦。
带说明的完整示例:https://codepen.io/mjurczyk/pen/wvBKOvP
@opsb 的答案很简洁,但中心点不准确,此外,正如@Jithin 指出的那样,如果角度是 360,则根本没有绘制任何内容。
@Jithin 修复了 360 度问题,但如果您选择的角度小于 360 度,那么您将得到一条关闭弧环的线,这不是必需的。
我修复了这个问题,并在下面的代码中添加了一些动画:
函数 myArc(cx, cy, 半径, max){ var circle = document.getElementById("arc"); var e = circle.getAttribute("d"); var d = " M "+ (cx + 半径) + " " + cy;变量角度=0; window.timer = window.setInterval( function() { var radians= angle * (Math.PI / 180); // 将度数转换为弧度 var x = cx + Math.cos(radians) * radius; var y = cy + Math.sin(弧度) * 半径; d += " L "+x + " " + y; circle.setAttribute("d", d) if(angle==max)window.clearInterval(window.timer); angle++ ; } ,5) } myArc(110, 110, 100, 360);
var e = circle.getAttribute("d");
行“什么都不做”?
我想对@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));
一张图片和一些 Python
只是为了更好地澄清并提供另一种解决方案。 Arc
[A
] 命令使用当前位置作为起点,因此您必须先使用 Moveto
[M] 命令。
那么Arc
的参数如下:
rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, xf, yf
例如,如果我们定义以下 svg 文件:
https://i.stack.imgur.com/oqPXx.png
您将使用 M
设置起点,使用 A
的参数 xf
和 yf
设置终点。
我们正在寻找圆,因此我们将 rx
设置为等于 ry
这样做基本上现在它会尝试找到与起点和终点相交的所有半径为 rx
的圆。
import numpy as np
def write_svgarc(xcenter,ycenter,r,startangle,endangle,output='arc.svg'):
if startangle > endangle:
raise ValueError("startangle must be smaller than endangle")
if endangle - startangle < 360:
large_arc_flag = 0
radiansconversion = np.pi/180.
xstartpoint = xcenter + r*np.cos(startangle*radiansconversion)
ystartpoint = ycenter - r*np.sin(startangle*radiansconversion)
xendpoint = xcenter + r*np.cos(endangle*radiansconversion)
yendpoint = ycenter - r*np.sin(endangle*radiansconversion)
#If we want to plot angles larger than 180 degrees we need this
if endangle - startangle > 180: large_arc_flag = 1
with open(output,'a') as f:
f.write(r"""<path d=" """)
f.write("M %s %s" %(xstartpoint,ystartpoint))
f.write("A %s %s 0 %s 0 %s %s"
%(r,r,large_arc_flag,xendpoint,yendpoint))
f.write("L %s %s" %(xcenter,ycenter))
f.write(r"""Z"/>""" )
else:
with open(output,'a') as f:
f.write(r"""<circle cx="%s" cy="%s" r="%s"/>"""
%(xcenter,ycenter,r))
你可以在我写的this post中有更详细的解释。
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;
};
const d = `M ${start.x} ${start.y} A ${radius} ${radius} 0 ${largeArc} 0 ${end.x} ${end.y}`
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);
使用扫描标志 =“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));
基于所选答案的 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;
我会使用其他答案中的代码,它们似乎都在相互复制,但我会将起点设为起点角度的函数,将终点设为终点角度的函数。
我将通过使用绝对值使大弧标志独立于顺序,并通过工作模 360 度使角度独立于数值大小。
var start = polarToCartesian(x, y, radius, startAngle);
var end = polarToCartesian(x, y, radius, endAngle);
largeArcFlag = Math.abs((endAngle - startAngle) % 360) <= 180 ? "0" : "1";
clockwiseFlag = (endAngle > startAngle) ? "1" : "0";
var d = [
"M", start.x, start.y,
"A", radius, radius, 0, largeArcFlag, clockwiseFlag, end.x, end.y
].join(" ");
向威利道歉;我没有读到最后,看到他发现了同样的事情。如果你喜欢我的帖子,就给他点个赞吧!
您可以使用我为上面的答案制作的 JSFiddle 代码:
https://jsfiddle.net/tyw6nfee/
您需要做的就是更改最后一行 console.log 代码并为其提供您自己的参数:
console.log(describeArc(255,255,220,30,180));
console.log(describeArc(CenterX,CenterY,Radius,startAngle,EndAngle))
不定期副业成功案例分享
arcSweep
变量实际上是控制large-arc-flag
svg A 参数。在上面的代码中,sweep-flag
参数的值始终为零。arcSweep
可能应该重命名为longArc
。endAngle - 0.0001
,如果没有,则不会渲染完整的弧。function describeArc(x,y,r,sAng,eAng){var M=Math,f=eAng-sAng<=180?0:1,q, cXY=(x,y,a)=>{q=(a-90)*M.PI/180;return[x+r*M.cos(q),y+r*M.sin(q)]}; return["M",...cXY(x,y,eAng), "A",r,r,0,f,0, ...cXY(x,y,sAng)].join(" ")}
示例用法:d=describeArc(200,200,100,-45,45);
返回半径为 100 像素(基于 200,200 的中心)从 -45° 到 45° 的圆弧的路径。 . 然后用d
的值填充<path>
标记的d
属性,例如myPath.setAttribute('d',d);
或 jQuery$('#myPath').attr('d',d);