【问题标题】:Very large HTML5 canvas circle imprecise非常大的 HTML5 画布圈不精确
【发布时间】:2012-02-01 15:29:39
【问题描述】:

我正在开发一个用户在 HTML5 画布上绘制欧几里得结构的应用程序。因此,我不能真正限制某些形状的大小。当探索在屏幕上绘制的非常大的圆圈时,我注意到非常大的圆圈没有恒定的半径。

更具体地说,由两个点定义的圆,一个中心点和一个指定半径的圆不再通过半径点!

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

错误不会发生在 45 度的倍数 = PI/4 上。在这些倍数之间误差最大(例如 PI/8)

这是一个包含上面第一个示例的 jsfiddle:

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

我的问题:为什么会发生这种情况?有没有办法(有效)解决这个问题?

【问题讨论】:

  • 在 MacOS X 10.7.2 上的 Chrome 16.0.912.63 上,您的小提琴中的圆圈触摸,但没有通过所需的点。
  • 有趣,顺便说一下,我在 Chrome 16.0.912.63 Windows 7 上。 Linux 上的 Chrome 也出现此问题。在 Firefox Windows 7 上会发生相同的错误,但要小一个数量级(仅当 r = 100 000 时才明显)。在 IE 9 上,错误甚至更小(在 r = 1 000 000 时明显)。所有这些测试都使用 alpha = PI/8

标签: graphics drawing html5-canvas


【解决方案1】:

我完全解决这个问题的方法是使用我自己的使用贝塞尔曲线绘制近似圆的实现。可以在此处找到详细介绍实现的文章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 canvas 中构建的实现更好地近似一个圆。

【讨论】:

  • 这比仅绘制圆的默认 arc() 方法要好得多。我用这个替换了 CanvasRenderingContext2D.prototype.arc,它修复了各种奇怪的伪影和故障。
【解决方案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/

【讨论】:

  • 哇,这真的很酷!但是,这并不能完全解决我的问题。我需要圆圈在任何地方都“完美”,而不仅仅是某个任意点。
  • 圆应该到处都是完美的。问题可能不在于圆的半径,而在于Math.sinMath.cos 的数学不精确性在乘以一个大常数时被夸大了。也就是说,使用原始方法放置任何点都会有问题,因此如果在 x+2*cos(a), y+2*sin(a) 放置第二个点,它将不会对齐。要获得真正准确的图表,您需要在旋转的画布上进行所有绘制。
  • 问题是Math.cosMath.sin是由V8中的一些taylor系列实现的。出于优化目的,这些泰勒级数的阶数不是很高,因此在绘制非常大的圆圈时会出现错误。
【解决方案3】:

只是把它扔在那里,但如果没有指定足够的 PI 数字,这可能是个问题吗?每当我做这样的事情时,我往往会有点过火并使用大约 10 位数的 PI。

【讨论】:

    【解决方案4】:

    在谷歌浏览器中我可以重复这个问题,但在 IE 9 和 IE 10 中一切都很好。

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

    也请看这里:HTML5 canvas arcs not rendering correctly in Google Chrome

    我能想象的唯一解决方法是通过您自己的代码或使用(jQuery?)为您执行此操作的插件来绘制圆圈。

    【讨论】:

      猜你喜欢
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-24
      相关资源
      最近更新 更多