Example of how to use zoom.extent and zoom.translateExtent to limit panning/zooming in d3 v4. The initial state of the visualisation are centered on coordinates [500, 1500] using zoom.transform. The limits for panning/zooming are set to [0, 5000].
Last active
December 17, 2024 07:11
Limit panning/zooming in d3 v4
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> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.js"></script> | |
<style> | |
svg text { | |
font-family: sans-serif; | |
font-size: 13px; | |
} | |
circle { | |
fill: transparent; | |
stroke: black; | |
stroke-width: 1px; | |
pointer-events:none; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="block"></div> | |
<script> | |
// dimensions | |
var dims = { | |
width: 800, | |
height: 300, | |
svg_dx: 100, | |
svg_dy: 100 | |
}; | |
// Data | |
var dataPoints = [1, 1010, 1020, 5000]; | |
// Zoom | |
var zoom = d3.zoom() | |
.extent([[dims.svg_dx, dims.svg_dy], [dims.width-(dims.svg_dx*2), dims.height-dims.svg_dy]]) | |
.scaleExtent([1, 10]) | |
.translateExtent([[dims.svg_dx, dims.svg_dy], [dims.width-(dims.svg_dx*2), dims.height-dims.svg_dy]]) | |
.on('zoom', zoomed); | |
// Scale | |
var xScale = d3.scaleLinear() | |
.domain([0, 5000]) | |
.range([dims.svg_dx, dims.width-(dims.svg_dx*2)]); | |
// Axis | |
var xAxis = d3.axisTop(xScale) | |
// Main svg | |
var gMain = d3.select('#block') | |
.append("svg") | |
.attr("width", dims.width) | |
.attr("height", dims.height) | |
.append("g") | |
.attr("transform", "translate(100, 100)"); | |
var rect = gMain | |
.append("rect") | |
.attr("x", 0) | |
.attr("y", -25) | |
.attr("width", dims.width) | |
.attr("height", 50) | |
.style("fill", "transparent") | |
.call(zoom); | |
// circles | |
var circles = gMain.selectAll('circle') | |
.data(dataPoints) | |
.enter() | |
.append('circle') | |
.attr('r', 7) | |
.attr('cx', function (d) { | |
return xScale(d); | |
}); | |
// axis | |
var axis = gMain.append("g") | |
// Jump to position (500, 1500) at start | |
startTransition(); | |
function zoomed() { | |
var transform = d3.event.transform; | |
// Zoom the circles | |
var xNewScale = transform.rescaleX(xScale); | |
circles | |
.attr("cx", function (d) { | |
return xNewScale(d); | |
}); | |
// Zoom the axis | |
xAxis.scale(xNewScale); | |
axis.call(xAxis); | |
} | |
function startTransition() { | |
// Position to (500, 1500) at start | |
// to jump to [500,1500] we need to calculate a new scale factor (k)... | |
var k = (xScale(5000) - xScale(0)) / (xScale(1500) - xScale(500)); | |
// ...and then a translate to [500, 0] | |
var tx = dims.svg_dx - (k * xScale(500)); | |
var t = d3.zoomIdentity.translate(tx, 0).scale(k); | |
// Rescale the axis | |
xAxis.scale(t.rescaleX(xScale)); | |
axis | |
.attr("transform", "translate(0,-20)") | |
.call(xAxis); | |
// Rescale the circles | |
rect.call(zoom.transform, t); | |
} | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment