This D3 example demonstrates using the zoom event and limits the bounds of the zooming to a specified domain. It is largely based on /jasondavies/3689931, but with bounds. Most of this bounding is done in the refresh function. You need to zoom in before you can pan or zoom out.
xxxxxxxxxx
<meta charset="utf-8">
<title>Constrained Zoom by Rectangle</title>
<script src="https://d3js.org/d3.v2.min.js?2.10.1"></script>
<style>
body {
font-family: sans-serif;
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
svg {
font: 9pt sans-serif;
shape-rendering: crispEdges;
}
rect {
fill: #ddd;
}
rect.zoom {
stroke: steelblue;
fill: #bbb;
fill-opacity: 0.5;
}
.axis path, .axis line {
fill: none;
stroke: #fff;
}
</style>
<p><label for="zoom-rect"><input type="checkbox" id="zoom-rect"> zoom by rectangle</label>
<script>
var margin = {top: 0, right: 12, bottom: 20, left: 60},
width = 960 - margin.left - margin.right,
height = 430 - margin.top - margin.bottom;
var xmin = 0,
xmax = 500,
ymin = 0,
ymax = 1000;
var x = d3.scale.linear()
.domain([xmin+.5, xmax+.5])
.range([0.5, width+.5]);
var y = d3.scale.linear()
.domain([ymin+.5, ymax+.5])
.range([height+.5, 0.5]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width); // tickLine == gridline
var refresh = function() {
var reset_s = 0;
if ((x.domain()[1] - x.domain()[0]) >= (xmax - xmin)) {
zoom.x(x.domain([xmin, xmax]));
reset_s = 1;
}
if ((y.domain()[1] - y.domain()[0]) >= (ymax - ymin)) {
zoom.y(y.domain([ymin, ymax]));
reset_s += 1;
}
if (reset_s == 2) { // Both axes are full resolution. Reset.
zoom.scale(1);
zoom.translate([0,0]);
}
else {
if (x.domain()[0] < xmin) {
x.domain([xmin, x.domain()[1] - x.domain()[0] + xmin]);
}
if (x.domain()[1] > xmax) {
var xdom0 = x.domain()[0] - x.domain()[1] + xmax;
x.domain([xdom0, xmax]);
}
if (y.domain()[0] < ymin) {
y.domain([ymin, y.domain()[1] - y.domain()[0] + ymin]);
}
if (y.domain()[1] > ymax) {
var ydom0 = y.domain()[0] - y.domain()[1] + ymax;
y.domain([ydom0, ymax]);
}
}
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
}
var zoom = d3.behavior.zoom().x(x).y(y).scaleExtent([.001, Infinity]).on("zoom", refresh);
var zoomRect = false;
d3.select("#zoom-rect").on("change", function() {
zoomRect = this.checked;
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom)
.append("g")
.on("mousedown", function() {
if (!zoomRect) return;
var e = this,
origin = d3.mouse(e),
rect = svg.append("rect").attr("class", "zoom");
d3.select("body").classed("noselect", true);
origin[0] = Math.max(0, Math.min(width, origin[0]));
origin[1] = Math.max(0, Math.min(height, origin[1]));
d3.select(window)
.on("mousemove.zoomRect", function() {
var m = d3.mouse(e);
m[0] = Math.max(0, Math.min(width, m[0]));
m[1] = Math.max(0, Math.min(height, m[1]));
rect.attr("x", Math.min(origin[0], m[0]))
.attr("y", Math.min(origin[1], m[1]))
.attr("width", Math.abs(m[0] - origin[0]))
.attr("height", Math.abs(m[1] - origin[1]));
})
.on("mouseup.zoomRect", function() {
d3.select(window).on("mousemove.zoomRect", null).on("mouseup.zoomRect", null);
d3.select("body").classed("noselect", false);
var m = d3.mouse(e);
m[0] = Math.max(0, Math.min(width, m[0]));
m[1] = Math.max(0, Math.min(height, m[1]));
if (m[0] !== origin[0] && m[1] !== origin[1]) {
zoom.x(x.domain([origin[0], m[0]].map(x.invert).sort(function(a,b) { return a - b;})))
.y(y.domain([origin[1], m[1]].map(y.invert).sort(function(a,b) { return a - b;})));
}
rect.remove();
refresh();
}, true);
d3.event.stopPropagation();
});
svg.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
</script>
Modified http://d3js.org/d3.v2.min.js?2.10.1 to a secure url
https://d3js.org/d3.v2.min.js?2.10.1