我使用start和endAngle渲染了svg圈.它工作得很好.但是当我渲染完整的圆圈(startAngle为70,endAngle为70)时,输出差别很大(0,90,180,270除外).我为这段代码做错了什么?
function getPathArc(center, start, end, radius) {
end -= this.isCompleteAngle(start, end) ? 0.0001 : 0;
var degree = end - start;
degree = degree < 0 ? (degree + 360) : degree;
return this.getCirclePath(
center, getLocationFromAngle(start, radius, center),
getLocationFromAngle(end, radius, center), radius, (degree < 180) ? 0 : 1
);
}
function getCirclePath(center, start, end, radius, clockWise) {
return 'M ' + start.x + ' ' + start.y + ' A ' + radius + ' ' +
radius + ' 0 ' + clockWise + ' 1 ' + end.x + ' ' + end.y;
}
function getLocationFromAngle(degree, radius, center) {
var radian = (degree * Math.PI) / 180;
return {
x : Math.cos(radian) * radius + center.x,
y : Math.sin(radian) * radius + center.y
}
}
function isCompleteAngle(startAngle, endAngle) {
var totalAngle = endAngle - startAngle;
totalAngle = totalAngle <= 0 ? (totalAngle + 360) : totalAngle;
return Math.floor(totalAngle / 360) !== 0;
}
示例链接:https://jsfiddle.net/fNPvf/43106/
绿色圆圈正确渲染,因为start和endAngle为90,即使我将角度更改为0,180,270和360也可以.但实际的问题是我使用70的红色圆圈甚至问题将会出现,除了那些角度(0,90,180,270,360).
如何清除这个问题?
解决方法:
圆之间的这种差异是由于数值精度的影响.由于SVG弧和浮点算法的工作方式,起点和终点的微小变化在最终弧中可能会被夸大.弧的角度越大,效果就越大.
要绘制圆弧,渲染器需要首先确定圆的中心.如果你试图制作一个360度的弧度,你的起点和终点几乎是一样的.
请考虑以下插图:
绿点和红点是弧的起点和终点.灰点是渲染计算以绘制圆弧的圆的中心.
图中的弧度大约为359度.现在想象一下将红色和绿色点移近.确定中心点所需的计算将越来越容易受到起点和终点坐标以及浮点算术函数的不准确性的影响.
除此之外,sin()和cos()函数只是sin和cos曲线的近似值.请记住,浏览器Javascript引擎必须平衡速度和准确性.
然后添加大多数(如果不是全部)SVG渲染引擎使用贝塞尔曲线逼近弧的事实.但是,beziers不能完美地代表圆弧.
希望你现在可以看到为什么你得到你的结果.尝试用单个弧表示大角度是一个坏主意.我的个人建议是使用至少三个或四个圆弧进行整圈.
function getPathArc(center, start, end, radius) {
if (end == start) end += 360;
var degree = end - start;
degree = degree < 0 ? (degree + 360) : degree;
var points = [];
points.push( getLocationFromAngle(start, radius, center) );
points.push( getLocationFromAngle(start+degree/3, radius, center) );
points.push( getLocationFromAngle(start+degree*2/3, radius, center) );
points.push( getLocationFromAngle(end, radius, center) );
return this.getCirclePath(points, radius, (degree < 180) ? 0 : 1);
}
function getCirclePath(points, radius, clockWise) {
return ['M', points[0].x, points[0].y,
'A', radius, radius, 0, 0, clockWise, points[1].x, points[1].y,
'A', radius, radius, 0, 0, clockWise, points[2].x, points[2].y,
'A', radius, radius, 0, 0, clockWise, points[3].x, points[3].y
].join(' ');
}
function getLocationFromAngle(degree, radius, center) {
var radian = (degree * Math.PI) / 180;
return {
x : Math.cos(radian) * radius + center.x,
y : Math.sin(radian) * radius + center.y
}
}
document.getElementById("arc1").setAttribute("d", getPathArc({x:250,y:250}, 90, 90, 200));
document.getElementById("arc2").setAttribute("d", getPathArc({x:250,y:250}, 70, 70, 200));
<svg width="500" height="500">
<path id="arc1" fill="none" stroke="green" stroke-width="8" />
<path id="arc2" fill="none" stroke="red" stroke-width="3" />
</svg>