2

我正在编写一个 JavaScript 应用程序,它在 HTML 画布上绘制任意形状的对象。用户应该能够通过单击它们来选择任何对象。

为了使它成为 O(1) 操作,我使用了一个阴影画布,即一个未显示的画布,它具有完全相同的大小,在普通画布上绘制的每个对象也被绘制在那里 - 但颜色代表它是ID.
所以一个简单的ghostContex.getImageData()连同鼠标点击坐标给了我那个像素的颜色,从而给我ID点击的对象的颜色。

所有这些都工作正常 - 除非我单击对象的确切边框。

由于它是在幽灵画布上使用抗锯齿绘制的,所以我得到了错误的颜色(因为该颜色是正确的颜色IDID之前绘制的对象的颜色之间的混合......)。这种错误的颜色代表错误ID,因此我选择了一个完全不同的对象:(

我该如何解决这个问题?

注意 #1:我已经在使用 translate(0.5, 0.5) 技巧来防止大多数抗锯齿
注意 #2:我之前尝试使用 SVG 编写此应用程序,但尤其是这个对象选择非常慢,因为我猜它是碰撞检测的对象太多。这就是我现在想要 O(1) 方法的主要原因......哦,这样我可以很容易地在幽灵画布上画一条比在普通画布上画的线大得多的线,从而使拾取更容易。
注意#3:相关浏览器是 Firefox、Chrome、Android 2.3+ 本机和 iOS 本机

4

3 回答 3

1

我不能在这里接受任何答案的原因很简单也很可悲:它不存在...... :(

抗锯齿不能切换,标准没有办法。但是该标准确实有一个命中测试功能(http://www.w3.org/html/wg/drafts/2dcontext/html5_canvas_CR/#hit-regions),它可以完全满足这里的需要。即使以一种很好的方式为开发人员隐藏了令人讨厌的细节——但它现在还没有在任何浏览器中实现。
并且实施看起来很遥远,直到不可能(参见例如评论 #6 在https://code.google.com/p/chromium/issues/detail?id=328961)。但显然它在上个月获得了动力......

那么在此期间可以做些什么呢?我做了什么?

在我的代码中,我可以为每个形状实现一个isPointInShape()方法。所以我使用幽灵画布技巧来获得一个形状并验证isPointInShape()我真的选择了正确的形状。这有助于抗锯齿像素不选择错误的形状(只需考虑单击形状#2 的边界,我们有 50% 的抗锯齿透明度 - 这会错误地告诉您选择了形状 #1...)。

如果实现泛型isPointInShape()对您的形状来说非常困难,您可以尝试我在其他地方读到的技巧(我没有尝试过,所以我没有测试它......):
创建一个额外的幽​​灵画布大小精确定位在鼠标位置的 1x1 像素。然后画出感兴趣的形状——当RGBA的A改变时,这个形状确实属于那个像素。

于 2014-06-08T20:40:08.330 回答
1

我知道这是一个旧帖子,但最近我遇到了类似的问题。我解决“两种颜色接缝”问题的方法是对辅助画布进行 10x10 像素采样,而不是单个像素。然后,我将 RGB 值字符串化,并将它们用作映射到颜色所代表对象的映射中的键。因此,最初使用 1 像素采样时,我立即使用贴图来确定关联的对象,但抗锯齿创建了贴图中不存在的中间颜色。10x10 方法通过循环返回的 100 个 RGB 值并创建“计数图”来解决此问题。此映射使用字符串化颜色并将它们映射到计数,但仅包括计数中第一个映射的有效颜色。所以你最终得到一张地图,上面写着你计算了 65 个红色像素和 23 个蓝色像素(剩下的 12 个像素是一些奇怪的抗锯齿混合)。在我计算颜色的同一个循环中,我还为当前最大计数和与该最大计数关联的当前颜色维护了一个变量(以避免再次循环通过这个新地图)。现在,最后您拥有了在 10x10 采样中计数最多的颜色,并且可以使用它映射回与其关联的对象。只有在 10x10 样本中没有找到有效颜色时,您才会得到未定义的结果,您可以合理地假设这意味着单击了“背景”。在我计算颜色的同一个循环中,我还为当前最大计数和与该最大计数关联的当前颜色维护了一个变量(以避免再次循环通过这个新地图)。现在,最后您拥有了在 10x10 采样中计数最多的颜色,并且可以使用它映射回与其关联的对象。只有在 10x10 样本中没有找到有效颜色时,您才会得到未定义的结果,您可以合理地假设这意味着单击了“背景”。在我计算颜色的同一个循环中,我还为当前最大计数和与该最大计数关联的当前颜色维护了一个变量(以避免再次循环通过这个新地图)。现在,最后您拥有了在 10x10 采样中计数最多的颜色,并且可以使用它映射回与其关联的对象。只有在 10x10 样本中没有找到有效颜色时,您才会得到未定义的结果,您可以合理地假设这意味着单击了“背景”。

于 2016-03-30T19:38:27.470 回答
0

我编了名字ghost context!你在用我的旧教程吗?:)

在那个旧教程中,我没有在每个对象被绘制到它之后清除幽灵上下文。在您的情况下,要解决您的问题,您可能需要在幽灵上下文上测试每个对象后清除。

当然,请确保您翻译的幻影上下文和正常上下文的数量完全相同。(然后将它们翻译回来,或重置转换)。

于 2013-05-01T17:20:24.020 回答