D3 zoom event firing on drag in Angular

2020-04-07 16:44发布

问题:


Tldr; dragging the SVG causes it to rotate as well as translate.


I am trying to implement dragging and zooming events on an SVG group using D3 (v.4) as part of an Angular service.

this.unitGroup = this.svg.append('g')
  .attr('id', 'unitGroup')
  .call(this.drag)
  .call(this.zoom);

Dragging translates the SVG.

drag = d3.drag()
.on('start', () => {
  console.log('drag start');
  this.setClickOrigin(d3.event);
})
.on('drag', (d, i, n) => {
  const target = d3.select(n[i]).node() as any;
  const m = target.getCTM();
  const x = d3.event.x - this.clickOrigin.x;
  const y = d3.event.y - this.clickOrigin.y;
  this.setClickOrigin(d3.event);
  this.translate(target, x, y);
});

While zooming rotates the SVG.

zoom = d3.zoom()
.on('zoom', (d, i, n) => {
  const target = d3.select(n[i]).node() as any;
  const m = target.getCTM();
  const b = target.getBBox();
  const dir = (d3.event.sourceEvent.deltaY > 0) ? 1 : -1;
  this.rotate(target, dir);
});

My original code worked fine. However, integrating it into Angular has thrown up some problems.

The current problem is that when you drag the unitGroup it triggers the zoom event along with the drag event.

The expected behaviour is that:

  • 'click-and-drag' translates the small, dark-grey box in the x and y dimensions.
  • 'mouse-wheel-scroll' rotates the small, dark-grey box around its center.

Here is a Plunker: https://embed.plnkr.co/0GrGG7T79ubpjYa2ChYp/

回答1:

Actually, what you are seeing here is the expected behaviour.

In D3, d3.zoom() handles not only the zoom but the panning as well. So, the mousemove is being handled by d3.drag() and by the zoom function as well.

As Bostock (D3 creator) once said:

combining these two behaviors* means that gesture interpretation is ambiguous and highly sensitive to position. (*zoom and drag)

Off the top of my head the simplest solution is just checking if you had a "real" zoom (mouse wheel) in the zoom function and, if you didn't (no mouse wheel), return:

if(!d3.event.sourceEvent.deltaY) return;

Here is your plunker with that change only: https://plnkr.co/edit/jz5X4Vm9wIzbKmTQLBAT?p=preview