Answers:
扩展@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" />
endAngle - 0.0001
,否则,将不会渲染完整的弧。
您要使用椭圆形A
rc命令。不幸的是,这要求您指定起点和终点的笛卡尔坐标(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)。到目前为止,给定这些约束,实际上可以绘制四个弧,因此两个标志选择其中一个。我猜您可能想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,请查看实现说明。他们描述了两种可能的圆弧参数化,“端点参数化”(他们选择的一种)和“中心参数化”(就像问题使用的一样)。在“端点参数化”的描述中,他们说:
端点参数化的优点之一是,它允许使用一致的路径语法,其中所有路径命令都以新的“当前点”的坐标结尾。
因此,基本上,这是电弧的副作用,它被视为较大路径的一部分,而不是其自己的单独对象。我想如果您的SVG渲染器不完整,它可以跳过任何不知道如何渲染的路径组件,只要知道它们接受多少参数即可。或者,它可以并行渲染具有许多组件的路径的不同块。或者,也许他们这样做是为了确保舍入错误不会沿着复杂路径的长度累积。
这些实现笔记对于原始问题也很有用,因为它们具有更多的数学伪代码,可用于在两个参数化之间进行转换(当我第一次编写此答案时,我并没有意识到)。
large-arc-flag
必须将其从0切换为1 的事实可能会引入一些错误。
我稍微修改了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的答案中添加了一个小扩展。
如果您想将此弧线转换为切片(以允许填充),我们可以稍作修改代码:
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>
然后你去!
@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);
<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>
我想对@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));
只是为了更好地阐明并提供另一种解决方案。 Arc
[ A
]命令使用当前位置作为起点,因此必须首先使用Moveto
[M]命令。
然后的参数Arc
如下:
rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, xf, yf
例如,如果我们定义以下svg文件:
<svg viewBox="0 0 500 500">
<path fill="red" d="
M 250 250
A 100 100 0 0 0 450 250
Z"/>
</svg>
您将设置与起点M
与参数的终点xf
和yf
的A
。
我们正在寻找圆,因此我们设置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))
在我写的这篇文章中,您可以有更详细的解释。
对于寻求答案的人(我也是),请注意- 如果不是必须使用arc,绘制局部圆的简单得多的解决方案是使用stroke-dasharray
SVG <circle>
。
将破折号数组分为两个元素,并将其范围缩放到所需角度。起始角度可使用调节stroke-dashoffset
。
看不到一个余弦。
带有说明的完整示例:https : //codepen.io/mjurczyk/pen/wvBKOvP
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));
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}`
基于所选答案的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))
arcSweep
变量实际上是在控制large-arc-flag
svg A参数。在上面的代码中,sweep-flag
参数的值始终为零。arcSweep
应该应该重命名为诸如此类longArc
。