D3 v4在同一元素上进行画笔和缩放(没有鼠标事件冲突) [英] D3 v4 Brush and Zoom on the same element (without mouse events conflicting)

查看:108
本文介绍了D3 v4在同一元素上进行画笔和缩放(没有鼠标事件冲突)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是建立一个同时使用 d3-zoom 和一起 d3-brush

  1. 当鼠标位于x轴上时,用户可以缩放(使用鼠标滚轮)或平移(通过向左和向右拖动)
  2. 当鼠标位于y轴上时,用户可以缩放(使用鼠标滚轮)或平移(通过上下拖动)
  3. 将鼠标置于统计图主体上方时,用户可以缩放(使用鼠标滚轮)或进行笔刷以将x域设置为特定范围

我已经设法在适当的轴上实现了缩放和平移(按照要求1和2),但是在将两者结合在一起以达到要求3时遇到了问题.

根据文档/示例,我已经完成了以下操作:

  • 要进行刷牙,请附加一个g SVG元素,然后在该元素上调用brush.该库在其中创建一个包含类overlayrect,并附加适当的鼠标事件以允许我在该空间内绘制画笔区域.

  • 要缩放,请附加一个rect SVG元素,然后在该元素上调用zoom.该库连接了适当的鼠标事件,以使我可以在该空间内使用鼠标滚轮上下滚动.

我遇到的问题是这两种方法的结合.因为zoombrush都创建了rect并处理鼠标事件,所以一次只能工作一个.最后创建的SVG元素会窃取"鼠标输入,并且不会将其传播到其他输入.我如何才能使两者在主图表区域完美搭配?

我有一个显示问题的示例项目.它基于Angular& TypeScript,但可以按以下方式运行:

  1. https://github.com/mattwilson1024/d3-zoom-和画笔
  2. 先运行npm installyarn install,然后运行npm startyarn start
  3. ,运行应用程序
  4. 打开http://localhost:4200
  5. 您可以在两个轴上缩放和平移,或在主图表区域上进行笔刷.可缩放区域的颜色为绿色和红色,以帮助调试
  6. 打开/src/app/char/chart.component.ts并取消注释线196.这将移动x轴缩放区域"(矩形)以覆盖整个图表.这意味着您可以使用滚轮在图表上的任意位置用鼠标进行x缩放.
  7. 问题在于,现在缩放区域从画笔窃取"了鼠标事件,因此不再可能进行画笔绘制,而是进行平移.除了希望d3-zoom设置的平移行为外,我希望能够通过拖动鼠标进行画刷,但仍然可以使用滚轮进行缩放.

解决方案

我最终也面对了这一挑战,设法找到了可行的解决方案. Zoom合并了event.stopPropagation().这意味着在父元素中包含的元素上触发的任何事件都不会冒泡"到父元素. 参见MDN以了解冒泡及其工作原理,并提供一个很好的示例

如果您将缩放行为附加到svg,以使其成为使用brush动作的group元素的祖先,则这将使g元素上的动作冒泡到svg.现在,随着事件冒泡,您的缩放也可以触发.在这种情况下,只需合并检查以查看事件的来源是什么,即是画笔操作还是缩放操作.根据检查结果,您可以应用缩放,如果且仅当缩放动作是触发缩放事件触发的原因时,否则可以应用刷涂方法.

My goal is to build a D3 (v4) chart that uses both d3-zoom and d3-brush together, as follows:

  1. When the mouse is positioned over the x-axis, the user can zoom (with the mouse wheel) or pan (by dragging left and right)
  2. When the mouse is positioned over the y-axis, the user can zoom (with the mouse wheel) or pan (by dragging up and down)
  3. When the mouse is positioned over the chart body, the user can zoom (with the mouse wheel) or brush to set the x domain to a specific range

I have managed to implement both zooming and panning on the appropriate axes (as per requirements 1 & 2), but am having problems combining the two together to achieve requirement 3.

I have done the following, based on the docs/examples:

  • For brushing, append a g SVG element and call the brush on that element. The library creates a rect with class overlay inside that, and attaches the appropriate mouse events to let me draw a brush region inside that space.

  • For zooming, append a rect SVG element and call the zoom on that element. The library hooks up the appropriate mouse events to allow me to scroll up and down with the mouse wheel inside that space.

The problem I have is in the combination of these two approaches. Because both zoom and brush create a rect and handle mouse events, only one works at a time. The SVG element that is created last "steals" the mouse input and does not propagate it to the other. How can I get the two to play nicely together on the main chart area?

I have an example project which shows the problem. It's based on Angular & TypeScript but can be run as follows:

  1. Checkout my example at https://github.com/mattwilson1024/d3-zoom-and-brush
  2. Run the app by running npm install or yarn install, then npm start or yarn start
  3. Open http://localhost:4200
  4. You can zoom&pan on the two axes, or brush the main chart area. The zoomable regions are coloured green and red to help debug
  5. Open /src/app/char/chart.component.ts and uncomment line 196. This moves the x-axis "zoom region" (rect) to cover the whole body of the chart. That means that you can use the wheel to zoom-x with the mouse anywhere over the chart.
  6. The problem is that now the zoom region "steals" the mouse event from the brush, so brushing is no longer possible, and it pans instead. Instead of the panning behaviour that d3-zoom sets up, I want to be able to brush by dragging the mouse, but still zoom with the wheel.

解决方案

I finally managed to get a working solution to this challenge as I was facing it too. Zoom incorporates event.stopPropagation(). This means that any events triggered on elements contained within a parent element will not "bubble up" to the parent element. See MDN to understand bubbling and how it works, with a nice example https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture

This is how I understand it working and what I have rationalised to get my solution working: For brushing, the event will propagate up to ancestor elements. In your case up to the svg-element. In your case you have appended a rect-element to the svg and attached your zoom behaviour to the rect. Therefore, the element with the zoom is not a parent of the element with the brush, but rather an "uncle/aunt", as if you traversed directly back up the DOM tree you would get back to the svg and never the rect.

If you instead attach the zoom behaviour to the svg so that it is an ancestor of the group element with the brush action, this will allow the action on the g element to bubble up to the svg. Now your zoom can also fire as the event will bubble up. In this case just incorporate a check to see what the source of the event was, i.e. was it the brush or zoom action. Based on the result of the check you can then apply the zoom, if and only if, the zoom action is what triggered the zoom event to fire, otherwise apply the brushing method.

这篇关于D3 v4在同一元素上进行画笔和缩放(没有鼠标事件冲突)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆