This gist shows how to restrict d3's zoom behavior so that users can't pan outside of a rectangular bounding box. Use your scroll wheel to zoom in and out of the field of circles, and click and drag to move when zoomed in. Note how when you zoom back out (by scrolling up) the view snaps to the original extent at zoom 1.
Last active
December 11, 2018 09:09
-
-
Save shawnbot/6518285 to your computer and use it in GitHub Desktop.
d3 bounded zoom behavior
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Restricted zoom behavior in d3</title> | |
<meta charset="utf-8"> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<style> | |
</style> | |
</head> | |
<body> | |
<script> | |
// first, define your viewport dimensions | |
var width = 960, | |
height = 500; | |
// then, create your svg element and a <g> container | |
// for all of the transformed content | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height) | |
.style("background-color", randomColor), | |
g = svg.append("g"); | |
// then, create the zoom behvavior | |
var zoom = d3.behavior.zoom() | |
// only scale up, e.g. between 1x and 50x | |
.scaleExtent([1, 50]) | |
.on("zoom", function() { | |
// the "zoom" event populates d3.event with an object that has | |
// a "translate" property (a 2-element Array in the form [x, y]) | |
// and a numeric "scale" property | |
var e = d3.event, | |
// now, constrain the x and y components of the translation by the | |
// dimensions of the viewport | |
tx = Math.min(0, Math.max(e.translate[0], width - width * e.scale)), | |
ty = Math.min(0, Math.max(e.translate[1], height - height * e.scale)); | |
// then, update the zoom behavior's internal translation, so that | |
// it knows how to properly manipulate it on the next movement | |
zoom.translate([tx, ty]); | |
// and finally, update the <g> element's transform attribute with the | |
// correct translation and scale (in reverse order) | |
g.attr("transform", [ | |
"translate(" + [tx, ty] + ")", | |
"scale(" + e.scale + ")" | |
].join(" ")); | |
}); | |
// then, call the zoom behavior on the svg element, which will add | |
// all of the necessary mouse and touch event handlers. | |
// remember that if you call this on the <g> element, the even handlers | |
// will only trigger when the mouse or touch cursor intersects with the | |
// <g> elements' children! | |
svg.call(zoom); | |
// then, let's add some circles | |
var circle = g.selectAll("circle") | |
.data(d3.range(300).map(function(i) { | |
return { | |
x: Math.random() * width, | |
y: Math.random() * height, | |
r: .01 + Math.random() * 50, | |
color: randomColor() | |
}; | |
}).sort(function(a, b) { | |
return d3.descending(a.r, b.r); | |
})) | |
.enter() | |
.append("circle") | |
.attr("fill", function(d) { return d.color; }) | |
.attr("cx", function(d) { return d.x; }) | |
.attr("cy", function(d) { return d.y; }) | |
.attr("r", function(d) { return d.r; }); | |
function randomColor() { | |
return "hsl(" + ~~(60 + Math.random() * 180) + ",80%,60%)"; | |
} | |
</script> | |
</body> | |
</html> |
Likewise, this saved me from a lot of frustration, thank you 😃
Thank you!
Nice stuff. If anyone like me wanted this to be bounded only in one dimension, i.e. zooming out until the max height is reached, but allowing that to be at any x-value (i.e. any width), you can just replace the tx line with
tx = e.translate[0]
to not constrain it. This allows you to use zoom with very wide charts. Also, if you're using the newest version of d3 (V4), you'll need to change a few things:
var e = d3.event,
tx = Math.min(0, Math.max(e.transform.x, width - width * e.transform.k)),
ty = Math.min(0, Math.max(e.transform.y, height - height * e.transform.k));
d3.selectAll('.toScale').attr('transform', [
"translate(" + [tx, ty] + ")",
"scale(" + e.transform.k + ")"
].join(" "));
Is there an example for scaling down?
Thank a lot, you saved my life
Thank you!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you. This gist saved my day.
I made a plunker with your name.
http://embed.plnkr.co/T0g2lK/preview