d3 v4 - zoom with buttons in conflicts with zoom behaviour
Asked Answered
C

1

6

I have created a gist with my problem.

I've started from this example.

So my problem is that I need some zoom in and zoom out buttons in addition to mouse controls.

Mouse controls (zooming with wheel and pan by dragging) are implemented with zoom() behaviour. It works pretty well.

Then I've added two buttons for zoom in and zoom out:

var _zoom = d3.zoom()
    .scaleExtent([1 / 2, 8])
    .on("zoom", zoomed);
var gui = d3.select("#gui")
gui.append("span")
  .classed("zoom in", true)
  .text("+")
  .on("click", function() { _zoom.scaleBy(container, 2); })
gui.append("span")
  .classed("zoom out", true)
  .text("-")
  .on("click", function(){ _zoom.scaleBy(container, 0.5); })

They are in conflict with the mouse behaviour. To reproduce the bug you have to zoom and drag around (with the mouse controls) and then click on the + span: the current transformation is overwritten.

How to resolve it?

Chilung answered 25/10, 2016 at 17:32 Comment(0)
J
18

The key thing to remember is that the zoom transform is stored on the node, NOT in the _zoom object.

In your case you set the mouse listener on the svg node,

svg.call(_zoom);

When a zoom event is triggered on the svg element, you then take the updated zoom transform of the svg node and apply it to the g node,

g.attr("transform", d3.event.transform);

All you need to do when updating the zoom of the g element on the button click is grab the correct transform, in your case that of the svg node,

 _zoom.scaleBy(svg, 0.5);

Which will trigger the zoom event on the svg node correctly, updated in full below,

var svg = d3.select("svg"),
  width = +svg.attr("width"),
  height = +svg.attr("height");
svg.style("border", "solid 1px black");
var points = d3.range(2000).map(phyllotaxis(10));
var g = svg.append("g");

var _zoom = d3.zoom()
  .scaleExtent([1 / 2, 8])
  .on("zoom", function() {
    g.attr("transform", d3.event.transform);
  });
svg.call(_zoom);

var container = g.selectAll("circle")
  .data(points)
  .enter().append("circle")
  .attr("cx", function(d) {
    return d.x;
  })
  .attr("cy", function(d) {
    return d.y;
  })
  .attr("r", 2.5)

function phyllotaxis(radius) {
  var theta = Math.PI * (3 - Math.sqrt(5));
  return function(i) {
    var r = radius * Math.sqrt(i),
      a = theta * i;
    return {
      x: width / 2 + r * Math.cos(a),
      y: height / 2 + r * Math.sin(a)
    };
  };
}
var gui = d3.select("#gui");
gui.append("span")
  .classed("zoom in", true)
  .text("+")
  .on("click", function() {
    _zoom.scaleBy(svg, 2);
  });
gui.append("span")
  .classed("zoom out", true)
  .text("-")
  .on("click", function() {
    _zoom.scaleBy(svg, 0.5);
  })
<svg width="960" height="500"></svg>
<div id="gui"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
Jaffa answered 27/10, 2016 at 19:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.