Skip to content

Instantly share code, notes, and snippets.

@shawnbot
Last active December 11, 2018 09:09
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save shawnbot/6518285 to your computer and use it in GitHub Desktop.
Save shawnbot/6518285 to your computer and use it in GitHub Desktop.
d3 bounded zoom behavior

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.

<!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>
@mehulumistry
Copy link

Thank a lot, you saved my life

@adriantdf
Copy link

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment