绘制到CanvasRenderingContext2D时的大小限制

时间:2015-02-10 18:52:29

标签: html5-canvas easeljs createjs

我已经很高兴地编辑了这个问题以提供更多细节

通过EaselJS框架绘制到CanvasRenderingContext2D时遇到了一个限制。我有这样的对象:

enter image description here

但是当这些物体的位置超过几百万像素时,图形开始崩溃。这是与x位置58524928相同的对象。(父容器移动到-58524928,以便我们可以在舞台上看到对象。)我越偏移对象,它就会越崩溃。此外,当我尝试移动对象时 - 用鼠标拖动它 - 它将“跳跃”,就像它受到一个大网格一样。

enter image description here

这是EaseJS框架,形状最终通过drawImage()方法绘制到CanvasRenderingContext2D。以下是代码片段:

ctx.drawImage(cacheCanvas,this._cacheOffsetX + this._filterOffsetX,this._cacheOffsetY + this._filterOffsetY,cacheCanvas.width / scale,cacheCanvas.height / scale);

我想这与JavaScript中有限数量的实数有关:

  

请注意,有无数的实数,但只有有限数   可以表示它们的数量(准确地说是18437736874454810627)   完全由JavaScript浮点格式。这意味着什么时候   你正在使用JavaScript中的实数来表示   数字通常是实际数字的近似值。

来源:JavaScript: The Definitive Guide

有人可以确认/拒绝我的假设吗? 5800万(58524928)对我来说似乎没那么多,它是EaselJS的一些低效率还是画布的限制?

PS: 缩放没有效果。我把所有东西都缩小了1000倍,接近1000倍而没有效果。同样,如果你将对象缩放1000倍,而仍然是x:5800万,它看起来不会崩溃。但是将它移到500亿,你就是你开始的地方。基本上偏移除以大小是细节的恒定限制。

修改

以下是jsfiddle.net/wzbsbtgc/2的示例。基本上有两个不同的问题

  1. 如果我使用大数字作为绘图本身的参数(红色曲线),它将会失真。这可以通过使用较小的数字并移动DisplayObject(蓝色曲线)来避免。
  2. 在这两种情况下都无法将DisplayObject移动1px。我认为这在GameAlchemist的帖子中有所解释。
  3. 欢迎提出第二个问题的建议/解决方法。

4 个答案:

答案 0 :(得分:2)

您看到的行为与数字在IEEE 754标准中的表示方式有关。

虽然Javascript使用64位浮点数,但WebGL仅使用32位浮点数,并且由于大多数(?all?)画布都是webGL加速的,因此所有数字都将在绘制之前(下)转换。

IEEE 754 32位标准使用32位来表示数字:1位用于符号,8位指数位,然后只有23位用于尾数。

我们打电话给IEEE_754_32max:

 IEEE_754_32max = ( 1 << 23 ) -1 = 8.388.6071  (8+ millions)  

我们只能在[-IEEE_754_32max, IEEE_754_32max]范围内获得整数的完全精度 除此之外,将使用指数,并且我们将松散尾数的弱位。

例如(10百万+ 1)= 10.000.001太大,它不能适合23位,所以它将被存储为

    10.000.001 = 10.000.00• * 10 = 1e7 = 10.000.000  

- 我们输掉了决赛&#39; 1&#39; -

您看到的网格效果与正在使用的指数/精确丢失相关联:使用58524928之类的数字,我们需要26位来表示该数字。因此丢失了3位,例如:

 58524928 + 7 == 58524928  

因此,当使用接近58524928的数字时,它将四舍五入为58524928,或者&#39; jump&#39;到最近的可能数字:你有网格效应。

解决方案?
- &GT;&GT;更改您用于应用程序的单位,以获得更小的数字。也许你正在使用mm - &gt;使用米或公里 请注意,您使用的精度是一种错觉:显示分辨率是第一个限制,鼠标最多只有1个像素精度,因此即使使用4K显示器,也不会有32位浮点数限制。

选择正确的测量单位,使您的所有坐标都适合较小的范围,您就可以解决问题。

更清楚:必须更改您使用作为显示的单位。这并不意味着你必须交易准确性:你必须在绘图之前自己进行翻译+缩放:这样你仍然使用Javascript IEEE 64位精度,你不再有那些32位舍入问题。

(您可以使用getters / setters

覆盖x,y属性
Object.defineProperty(targetObject, 'x', { get : function() { return view.pixelWidth*(this.rx-view.left)/view.width ;  } }

答案 1 :(得分:2)

Context2D似乎使用较低精度的数字进行变换。我还没有确认精确度,但我的猜测是它使用浮动而不是双打。

因此,使用更高的值,EaselJS依赖的transform方法(以及其他类似的C2D方法)会失去精度,类似于GameAlchemist所描述的。您可以在此处使用纯C2D调用重现此问题: http://jsfiddle.net/9fLff2we/

我能想到的最好的解决方法是预先计算&#34;最终&#34;变换方法外部的值。正常的JS数字比C2D使用的精度更高,因此这应该可以解决问题。非常粗略的例子来说明: http://jsfiddle.net/wzbsbtgc/3/

答案 2 :(得分:1)

您可以使用所需的任何尺寸的绘图坐标。

Canvas会将您的绘图剪辑到canvas元素的显示区域。

例如,这是一个开始从x = -50000000绘制一条线并在画布上结束的演示。仅渲染线条的可见部分。所有不可见(非画布)点都被剪裁。

&#13;
&#13;
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

ctx.beginPath();
ctx.moveTo(-50000000,100);
ctx.lineTo(150,100);
ctx.stroke();
&#13;
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
&#13;
<h4>This line starts at x = negative 50 million!</h4>
<canvas id="canvas" width=300 height=300></canvas>
&#13;
&#13;
&#13;

答案 3 :(得分:1)

请记住,W3C标准的目标受众主要是浏览器供应商。无符号长值(或2 32 )更多地解决了浏览器创建位图的基础系统。该标准表示此范围内的值是有效的,但无法保证底层系统能够提供大的位图(当今大多数浏览器将位图限制为比此更小的位图)。你声明你并不是指canvas元素本身,但链接引用是元素的接口定义,所以我只是想指出数字范围。

从JavaScript方面来看,我们开发人员通常都是这样,除了类型化数组之外,没有像ulong那样的东西等只有Number(又名unrestricted double)才签名并以64位格式存储数字,格式为IEEE-754。

Number的有效范围是:

Number.MIN_VALUE = 5e-324
Number.MAX_VALUE = 1.7976931348623157e+308

您可以将此范围内的任何值与画布一起用于矢量路径。当路径被光栅化时,Canvas会根据当前的变换矩阵将它们剪辑到位图。

如果您通过绘制意味着另一个位图(即图像,画布,视频),那么它们将受到与目标画布本身相同的系统和浏览器功能/限制。定位(直接或通过转换)受到数字范围的限制(总和)。