非常大的HTML5画布圆形不精确

6
我正在开发一个应用程序,用户在HTML5画布上绘制欧几里得构造。因此,我无法限制某些形状的大小。当探索在屏幕上绘制非常大的圆时,我注意到非常大的圆不具有恒定的半径。
更具体地说,由两个点定义的圆,一个中心点和一个指定半径的点不再通过半径点!

Large circle with radius point

逐渐变大的圆。这些圆都应该通过点E。

Larger circles

错误不会在45度的倍数处发生= PI/4。在这些倍数之间,误差最大(例如PI/8)
这里有一个包含上述第一个示例的jsfiddle:

http://jsfiddle.net/D28J2/2/

我的问题是:为什么会发生这种情况?有没有一些(有效的)方法可以解决这个问题?

1
在MacOS X 10.7.2上的Chrome 16.0.912.63中,您在fiddle中的圆圈“接触”但未通过所需点。 - Alnitak
1
有趣的是,我正在使用Chrome 16.0.912.63 Windows 7。此问题也在Linux上的Chrome中发生。在Firefox Windows 7上,相同的错误发生,但数量级更小(仅当r = 100,000时才会注意到)。在IE 9上,误差甚至更小(在r = 1,000,000时才能注意到)。所有这些测试都是在alpha = PI / 8下进行的。 - Mathijs Henquet
4个回答

3
我曾经遇到的问题是绕过圆形绘制时出现了问题。我完全解决了这个问题,使用贝塞尔曲线来近似绘制圆形。可以在这里找到详细的实现方法: http://www.tinaja.com/glib/ellipse4.pdf
function magic_circle(ctx, x, y, r){
  m = 0.551784

  ctx.save()
  ctx.translate(x, y)
  ctx.scale(r, r)

  ctx.beginPath()
  ctx.moveTo(1, 0)
  ctx.bezierCurveTo(1,  -m,  m, -1,  0, -1)
  ctx.bezierCurveTo(-m, -1, -1, -m, -1,  0)
  ctx.bezierCurveTo(-1,  m, -m,  1,  0,  1)
  ctx.bezierCurveTo( m,  1,  1,  m,  1,  0)
  ctx.closePath()
  ctx.restore()
}

仅使用这四个片段,我就能比 Google Chrome 内置的画布实现更好地近似圆形。


1
这比默认的arc()方法要好得多,只绘制圆形。我用这个替换了CanvasRenderingContext2D.prototype.arc,解决了各种奇怪的伪像和故障问题。 - mclaassen

2
这可能是浮点数截断错误。可能是因为正弦和余弦函数未提供完全准确的值。可以通过旋转画布而不是圆弧来解决这个问题(至少在Chrome中可行)。
ctx.save();          // Save the canvas so we can rotate back.
ctx.translate(x, y); // Translate to the origin point.
ctx.rotate(alpha);   // Rotate the proper angle.

ctx.arc(0, 0, 3, 0, Math.PI*2); // Draw the small circle at the origin.
ctx.fill();

ctx.arc(r, 0, r, 0, Math.PI*2); // Create a big with the origin 1 radius away.
ctx.restore();                  // Restore the canvas to the original orientation
                                // before drawing.  Otherwise the circle looks bad.
ctx.strokeStyle = "black";
ctx.stroke();                   // Draw!

我很喜欢操作画布而不是形状。它给你一个更加逻辑的工作区域。请参见http://jsfiddle.net/D28J2/10/


1
哇,那真的很酷!然而,那并没有完全解决我的问题。我需要圆在任何地方都是“完美”的,而不仅仅是某些任意点。 - Mathijs Henquet
圆应该无处不完美。问题可能不在于圆的半径,而在于当乘以一个大常数时,Math.sinMath.cos的数学不精确性被夸大了。话虽如此,使用原始方法放置任何点都会有问题,因此如果您在x+2cos(a),y+2sin(a)处放置第二个点,则它不会对齐。为了获得真正准确的图形,您需要在旋转的画布上进行所有绘制。 - Brian Nickel
1
问题在于Math.cosMath.sin在V8中是通过一些泰勒级数实现的。为了优化目的,这些泰勒级数的阶数不是很高,因此在绘制非常大的圆时会出现误差。 - Mathijs Henquet

1

我只是随便说一下,但是没有指定足够的圆周率数字会不会成为一个问题?每当我做这样的事情时,我倾向于有些过度,并使用约10个圆周率数字。


0

在Google Chrome中,我可以重现这个问题,但在IE 9和IE 10中一切正常。

所以我的猜测是Chrome的实现有误。可能是一个舍入误差或者他们使用了插值方法来避免sin和cos,这并不是非常准确的。

在这里也看看:HTML5画布圆弧在Google Chrome中无法正确渲染

我唯一能想到的解决方法是通过自己的代码绘制圆形或使用一个(jQuery?)插件来代替。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接