d3 v4 - zoom with buttons in conflicts with zoom behaviour
d3.js, javascript, pan, zooming
Solution
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>
Problem
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?