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?
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With