Skip to content

Instantly share code, notes, and snippets.

@GerardoFurtado
Created May 5, 2019 00:32
Show Gist options
  • Save GerardoFurtado/67474234397677712c0608c4a7ce574c to your computer and use it in GitHub Desktop.
Save GerardoFurtado/67474234397677712c0608c4a7ce574c to your computer and use it in GitHub Desktop.
d3 bounded zoom behavior
license: mit

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.

forked from shawnbot's block: d3 bounded zoom behavior

<!DOCTYPE html>
<html>
<head>
<title>Restricted zoom behavior in d3</title>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.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.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.transform,
// now, constrain the x and y components of the translation by the
// dimensions of the viewport
tx = Math.min(0, Math.max(e.x, width - width * e.k)),
ty = Math.min(0, Math.max(e.y, height - height * e.k));
// then, update the zoom behavior's internal translation, so that
// it knows how to properly manipulate it on the next movement
// 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.k + ")"
].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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment