Skip to content

Instantly share code, notes, and snippets.

@garrilla
Forked from mbostock/.block
Last active February 11, 2016 07:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garrilla/11280861 to your computer and use it in GitHub Desktop.
Save garrilla/11280861 to your computer and use it in GitHub Desktop.
Zoom + Pan with Limiting

This is an example of pan limiting, trapping for zoom.translate() by passing an array, which is determined by a simple boolean network for each of x and y of the translation to ensure that dragging is constrained within the panExtent.

It is a fork of Pan+Zoom demonstrating an example of d3.behavior.zoom applied using x- and y-scales.

Requirements

  1. create an object variable var panExtent = {x: [0,600], y: [-200,Infinity] }; to set the limits of the pan

  2. when setting the domain test for compliance with the panExtent (the domain should be no bigger than panExtent)

  3. add function panLimit() to do the limiting Math which returns an array used by zoom.translate()

  4. in the listener for the zoom (in this case the function zoomed()) set the zoom.translate(panLimit()) vector.

note: (fixed) I just noticed this only works effectively for the lower bound when the zoom.scale() is 1 or the lower Y bound is 0, I'll fix this soon.

<!DOCTYPE html>
<meta charset="utf-8">
<title>Zoom + Pan with Limiting</title>
<style>
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
rect {
fill: #ddd;
}
.axis path, .axis line {
fill: none;
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
/*
insertion: add object variable for panExtent
## range of panExtent = {x: [-Infinity,Infinity], y: [-Infinity,Infinity] };
*/
var panExtent = {x: [0,width], y: [-100,400] };
/*
change bounds
## test the extent and choose appropriate scales
*/
var x = d3.scale.linear()
.domain([panExtent.x[0]>(-width / 2)?panExtent.x[0]:(-width / 2),panExtent.x[1]<(width / 2)?panExtent.x[1]:(width / 2)])
.range([0, width]);
var y = d3.scale.linear()
.domain([panExtent.y[0]>(-height / 2)?panExtent.y[0]:(-height / 2),panExtent.y[1]<(height / 2)?panExtent.y[1]:(height / 2)])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width);
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 10])
.on("zoom", zoomed);
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);
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);
function zoomed() {
/* call the zoom.translate vector with the array returned from panLimit() */
zoom.translate(panLimit());
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
}
function panLimit() {
/*
include boolean to work out the panExtent and return to zoom.translate()
*/
var divisor = {h: height / ((y.domain()[1]-y.domain()[0])*zoom.scale()), w: width / ((x.domain()[1]-x.domain()[0])*zoom.scale())},
minX = -(((x.domain()[0]-x.domain()[1])*zoom.scale())+(panExtent.x[1]-(panExtent.x[1]-(width/divisor.w)))),
minY = -(((y.domain()[0]-y.domain()[1])*zoom.scale())+(panExtent.y[1]-(panExtent.y[1]-(height*(zoom.scale())/divisor.h))))*divisor.h,
maxX = -(((x.domain()[0]-x.domain()[1]))+(panExtent.x[1]-panExtent.x[0]))*divisor.w*zoom.scale(),
maxY = (((y.domain()[0]-y.domain()[1])*zoom.scale())+(panExtent.y[1]-panExtent.y[0]))*divisor.h*zoom.scale(),
tx = x.domain()[0] < panExtent.x[0] ?
minX :
x.domain()[1] > panExtent.x[1] ?
maxX :
zoom.translate()[0],
ty = y.domain()[0] < panExtent.y[0]?
minY :
y.domain()[1] > panExtent.y[1] ?
maxY :
zoom.translate()[1];
return [tx,ty];
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment