检测 HTML 画布中某些点的鼠标悬停?
Posted
技术标签:
【中文标题】检测 HTML 画布中某些点的鼠标悬停?【英文标题】:Detect mouseover of certain points within an HTML canvas? 【发布时间】:2010-11-16 07:41:18 【问题描述】:我已经为 Canvas 构建了一个分析数据可视化引擎,并被要求在数据元素上添加类似工具提示的悬停,以显示光标下数据点的详细指标。
对于简单的条形图和甘特图、树形图和具有简单方形区域或特定兴趣点的节点图,我能够通过使用 :hover 属性覆盖绝对定位的 DIV 来实现这一点,但还有一些更复杂的可视化,例如作为饼图和交通流渲染,其中有数百个由贝塞尔曲线定义的独立区域。
是否可以以某种方式附加叠加层,或者当用户将鼠标悬停在特定的封闭路径上时触发事件?
每个需要指定悬停的区域定义如下:
context.beginPath();
context.moveTo(segmentRight, prevTop);
context.bezierCurveTo(segmentRight, prevTop, segmentLeft, thisTop, segmentLeft, thisTop);
context.lineTo(segmentLeft, thisBottom);
context.bezierCurveTo(segmentLeft, thisBottom, segmentRight, prevBottom, segmentRight, prevBottom);
/*
* ...define additional segments...
*/
// <dream> Ideally I would like to attach to events on each path:
context.setMouseover(function()/*Show hover content*/);
// </dream>
context.closePath();
绑定到这样的对象在 Flash 或 Silverlight 中实现几乎是微不足道的,因为当前的 Canvas 实现具有直接使用我们现有的 javascript API 并与其他 Ajax 元素集成的优势,我们希望避免将 Flash 放入混合。
有什么想法吗?
【问题讨论】:
这里有一些关于 html5 Canvas 事件处理的好教程html5canvastutorials.com/advanced/html5-canvas-image-events 你可以试试KineticJS。 【参考方案1】:您可以处理 mousemove 事件并从该事件中获取 x,y 坐标。然后,您可能必须遍历所有路径以测试该点是否在路径上。我有一个similar problem,可能有一些你可以使用的代码。
以这种方式循环操作可能会很慢,尤其是在 IE 上。您可能会加快速度的一种方法 - 这是一种技巧,但它会非常有效 - 将更改绘制每条路径的颜色,以便人类不会注意到它,但这样可以绘制每条路径不同的颜色。有一个表格来查找路径的颜色,只需查找鼠标下像素的颜色。
【讨论】:
我喜欢颜色的想法,但这种可视化使用大量渐变来指示不同的活动点。我在twitpic.com/cqam4 找到了一张图片,展示了我的意思。对于每个彩色区域,我希望鼠标悬停以通过名称识别它并显示有关问题点的一些数字统计信息。 我真的很喜欢颜色的想法。现在,每个渐变在特定的色调范围内都是不同的,所以我可以解码回 HSV 并以这种方式进行映射!我会试试看。 精度太低了。我得到类似颜色区域的颜色范围溢出。相反,我想我会渲染一个单独的不可见阴影画布,其中包含直接映射的非渐变颜色值,并在那里执行像素颜色索引查找。 您可以使用第二个不可见的画布进行颜色编码。 我想知道如何在画布形状上检测鼠标悬停,并考虑使用 x,y 查找方法,很高兴看到您对颜色的评论,因为我记得 Clutter 库还使用独特的颜色来“挑选”鼠标事件上的元素,除了我相信它会在舞台外镜像和绘制形状,因此用户看不到颜色,这也可能适用于画布情况。【参考方案2】:阴影画布
我在其他地方看到的用于鼠标悬停检测的最佳方法是将要检测的绘图部分重复到隐藏的、已清除的画布上。然后存储 ImageData 对象。然后,您可以检查感兴趣的像素的 ImageData 数组,如果 alpha 值大于 0,则返回 true。
// slow part
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillRect(100,100,canvas.width-100,canvas.height-100);
var pixels = ctx.getImageData(0,0,canvas.width,canvas.height).data;
// fast part
var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
if (pixels[idx]) // alpha > 0
...
优势
您可以检测到任何您想要的东西,因为您只是在重复上下文方法。这适用于 PNG alpha、疯狂的复合形状、文本等。 如果您的图像相当静态,那么您只需为每个感兴趣区域执行一次此操作。 “蒙版”很慢,但查找像素非常便宜。因此,“快速部分”非常适合鼠标悬停检测。缺点
这是一个内存猪。每个掩码是 W*H*4 值。如果您的画布区域很小或要遮盖的区域很少,那还不错。使用 chrome 的任务管理器监控内存使用情况。 目前在 Chrome 和 Firefox 中存在与 getImageData 相关的已知问题。如果您使变量无效,结果不会立即被垃圾收集,因此如果您过于频繁地这样做,您会看到内存迅速增加。它最终会被垃圾收集起来,而且它不应该让浏览器崩溃,但它可能会对具有少量 RAM 的机器造成负担。节省内存的技巧
我们可以只记住哪些像素具有 alpha 值,而不是存储整个 ImageData 数组。它节省了大量内存,但在掩码过程中增加了一个循环。
var mask = ;
var len = pixels.length;
for (var i=3;i<len;i+=4) if ( pixels[i] ) mask[i] = 1;
// this works the same way as the other method
var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
if (mask[idx])
...
【讨论】:
【参考方案3】:这可以使用方法 ctx.isPointInPath 来完成,但它没有在 IE 的 ExCanvas 中实现。 但另一种解决方案是使用 HTML 地图,就像我为这个小库所做的那样:http://phenxdesign.net/projects/phenx-web/graphics/example.htm 你可以从中获得灵感,但它仍然有点问题。
【讨论】:
哦,我认为 isPointInPath 是我正在寻找的,因为无论如何我们都不想与 IE 兼容(内部使用工具)。谢谢! HTML 地图与我为一些更简单的可视化所做的类似,但对于非常复杂的贝塞尔路径,我宁愿坚持使用画布中的内容。【参考方案4】:我需要为正方形网格(如 Excel 电子表格的单元格)检测鼠标点击。为了加快速度,我将网格划分为递归减半的区域,直到剩下少量单元格,例如对于 100x100 网格,前 4 个区域可能是包含四个象限的 50x50 网格。 然后这些可以分成另外 4 个(因此给出 16 个区域,每个区域为 25x25)。 这需要进行少量比较,最后可以为每个单元格测试 25x25 网格(本例中为 625 次比较)。
【讨论】:
【参考方案5】:Eric Rowell 有一本书名为“HTML5 CANVAS COOKBOOK”。在那本书中有一章名为“与画布交互:将事件侦听器附加到形状和区域”。可以实现 mousedown、mouseup、mouseover、mouseout、mousemove、touchstart、touchend 和 touchmove 事件。我强烈建议您阅读。
【讨论】:
问题比书早两年,哈哈。谢谢。【参考方案6】:这无法完成(嗯,至少不是那么容易),因为您在画布上绘制的对象(路径)在画布中不会表示为相同的对象。我的意思是它只是一个简单的 2D 上下文,一旦你在上面画了一些东西,它就会完全忘记它是如何绘制的。它只是一组像素。
为了观看鼠标悬停之类的内容,您需要某种矢量图形画布,即 SVG 或在现有的基础上实现自己的(这是 Sam Hasler 建议的)
【讨论】:
【参考方案7】:我建议覆盖一个图像地图,并在区域上设置适当的坐标以匹配您在画布上绘制的项目。这样,您就可以免费获得工具提示和大量其他 DOM/浏览器功能。
【讨论】:
以上是关于检测 HTML 画布中某些点的鼠标悬停?的主要内容,如果未能解决你的问题,请参考以下文章