繁体   English   中英

检测HTML画布中某些点的鼠标悬停?

[英]Detect mouseover of certain points within an HTML canvas?

我已经为Canvas构建了一个分析数据可视化引擎,并且已经被要求在数据元素上添加类似工具提示的悬停,以显示光标下数据点的详细度量。

对于简单的条形图和Gaant图表,具有简单方形区域或特定兴趣点的树形图和节点图,我能够通过将绝对定位的DIV与:悬停属性重叠来实现这一点,但是还有一些更复杂的可视化,例如饼图以及由bezeir曲线定义的数百个独立区域的交通流渲染。

是否有可能以某种方式附加叠加层,或在用户将鼠标悬停在特定的封闭路径上时触发事件?

需要指定悬停的每个区域定义如下:

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置于混合中。

有任何想法吗?

您可以处理mousemove事件并从事件中获取x,y坐标。 然后,您可能必须遍历所有路径以测试该点是否在路径上。 我有一个类似的问题 ,可能有一些你可以使用的代码。

以这种方式循环事物可能会很慢,尤其是在IE上。 一种方法可以加速它 - 这是一个黑客,但它会非常有效 - 将改变每个路径绘制的颜色,以便人类不会注意到,但每个路径都是绘制的不同的颜色。 有一个表来查找路径的颜色,只需查看鼠标下的像素颜色。

阴影画布

我在其他地方看到的用于鼠标悬停检测的最佳方法是将要检测的绘图部分重复到隐藏的清除画布上。 然后存储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]) {
  ...
}

这可以使用方法ctx.isPointInPath完成,但它不是在ExCanvas for IE中实现的。 但另一个解决方案是使用HTML地图,就像我为这个小库所做的那样: http//phenxdesign.net/projects/phenx-web/graphics/example.htm你可以从中获得灵感,但它仍然是一点点越野车。

我建议覆盖一个图像地图,并在区域上设置适当的坐标,以匹配画布绘制的项目。 这样,您可以免费获得工具提示和许多其他DOM /浏览器功能。

我需要检测鼠标点击方块的网格(如excel电子表格的单元格)。 为了加快速度,我将网格划分为递归减半的区域,直到剩余少量单元格,例如100x100网格,前4个区域可以是包含四个象限的50x50网格。 然后可以将它们分成另外4个(因此给出16个区域,每个区域25x25)。 这需要进行少量比较,最后可以针对每个单元测试25x25网格(在该示例中为625个比较)。

Eric Rowell有一本名为“HTML5 CANVAS COOKBOOK”的书。 在那本书中有一章名为“与画布交互:将事件监听器附加到形状和区域”。 可以实现mousedown,mouseup,mouseover,mouseout,mousemove,touchstart,touchend和touchmove事件。 我强烈建议你阅读。

这是不可能的(好吧,至少不那么容易),因为你在画布上绘制的对象(路径)不会在画布中表示为相同的对象。 我的意思是它只是一个简单的2D上下文,一旦你在它上面画了一些东西,它就会完全忘记它的绘制方式。 它只是一组像素。

为了观看鼠标悬停和它的喜欢,你需要某种矢量图形画布,即SVG或在现有的基础上实现你自己的(这是Sam Hasler建议的)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM