如何使用start和endAngle渲染svg圈 [英] How to render a svg circle using start and endAngle

查看:81
本文介绍了如何使用start和endAngle渲染svg圈的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用start和endAngle渲染了svg圈。它工作得很好。但是当我渲染完整的圆圈(startAngle为70,endAngle为70)时,输出差别很大(0,90,180,270除外)。我错误地为这段代码做了什么?

  function getPathArc(center,start,end,radius){
end - = this.isCompleteAngle(开始,结束)? 0.0001:0;
var degree = end - start;
degree = degree< 0? (学位+ 360):学位;
返回this.getCirclePath(
center,getLocationFromAngle(start,radius,center),
getLocationFromAngle(end,radius,center),radius,(degree< 180)?0:1
);
}

函数getCirclePath(中心,开始,结束,半径,clockWise){
返回'M'+ start.x +''+ start.y +'A' + radius +''+
radius +'0'+ clockWise +'1'+ end.x +''+ end.y;
}

函数getLocationFromAngle(度,半径,中心){
var radian =(degree * Math.PI)/ 180;
返回{
x:Math.cos(弧度)* radius + center.x,
y:Math.sin(弧度)* radius + center.y
}
}

function isCompleteAngle(startAngle,endAngle){
var totalAngle = endAngle - startAngle;
totalAngle = totalAngle< = 0? (totalAngle + 360):totalAngle;
返回Math.floor(totalAngle / 360)!== 0;
}

示例链接:



正确渲染绿色圆圈因为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;度=度< 0? (学位+ 360):学位; 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',半径,半径,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 ,中心){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 =500height = 500 > < path id =arc1fill =nonestroke =greenstroke-width =8/> < path id =arc2fill =nonestroke =redstroke-width =3/>< / svg>  


I have rendered the svg circle using start and endAngle. It was worked fine. But when i render the complete circle(startAngle as 70 and endAngle as 70) the output is huge different(except the 0, 90, 180, 270). what i made wrongly for this code?

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

Sample Link: https://jsfiddle.net/fNPvf/43106/

The Green circle is rendered correctly because the start and endAngle is 90 even if i change the angle as 0, 180, 270 and 360 it will work. but The actual problem is Red circle which i use 70 even the issue will come except those angles(0, 90, 180, 270, 360).

how to clear this issue?

解决方案

This difference between the circles is due to the effects of numerical accuracy. Due to the way SVG arcs and floating point arithmetic works, minuscule changes in the start and end points can be exaggerated in the final arc. The bigger the angle that your arc spans, the more the effect comes into play.

To draw the arc, the renderer needs to first determine the centre of the circle. If you try to make an arc of 360 degrees, your start and end points are going to be almost the same.

Consider the following illustration:

The green and red points are the start and end points of the arc. The grey point is the centre of the circle that the render calculates in order to draw the arc.

The arc in the illustration represents roughly 359 degrees. Now imagine moving the red and the green points closer together. The calculation required to determine the centre point is going to become more and more susceptible to inaccuracies in the start and end coordinates and in the floating point arithmetic functions.

Add to that the fact that the sin() and cos() functions are only approximations of the sin and cos curves. Remember that browser Javascript engines have to balance speed and accuracy.

Then add to that the fact that most (if not all) SVG rendering engines approximate arcs using bezier curves. But beziers can not perfectly represent a circular arc.

Hopefully you can now see why you are getting the results you are. Trying to represent large angles with a single arc is a bad idea. My personal recommendation is use at least three or four arcs for a full circle.

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>

这篇关于如何使用start和endAngle渲染svg圈的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆