我已经很高兴地编辑了这个问题以提供更多细节
通过EaselJS框架绘制到CanvasRenderingContext2D时遇到了一个限制。我有这样的对象:
但是当这些物体的位置超过几百万像素时,图形开始崩溃。这是与x位置58524928相同的对象。(父容器移动到-58524928,以便我们可以在舞台上看到对象。)我越偏移对象,它就会越崩溃。此外,当我尝试移动对象时 - 用鼠标拖动它 - 它将“跳跃”,就像它受到一个大网格一样。
这是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的示例。基本上有两个不同的问题
欢迎提出第二个问题的建议/解决方法。
答案 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绘制一条线并在画布上结束的演示。仅渲染线条的可见部分。所有不可见(非画布)点都被剪裁。
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;
答案 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会根据当前的变换矩阵将它们剪辑到位图。
如果您通过绘制意味着另一个位图(即图像,画布,视频),那么它们将受到与目标画布本身相同的系统和浏览器功能/限制。定位(直接或通过转换)受到数字范围的限制(总和)。