An example of setting x- and y-scale domains (zooming) by selecting a
rectangular region using the mouse. The original question required that it
work in tandem with d3.behavior.zoom for panning, hence the checkbox for
switching on the “zoom by rectangle” mode.
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <title>Zoom by Rectangle</title> | |
| <script src="http://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: 10px sans-serif; | |
| shape-rendering: crispEdges; | |
| } | |
| rect { | |
| fill: #ddd; | |
| } | |
| rect.zoom { | |
| stroke: steelblue; | |
| 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: 12, left: 36}, | |
| width = 960 - margin.left - margin.right, | |
| height = 430 - margin.top - margin.bottom; | |
| var x = d3.scale.linear() | |
| .domain([-width / 2, width / 2]) | |
| .range([0, width]); | |
| var y = d3.scale.linear() | |
| .domain([-height / 2, 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).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())) | |
| .y(y.domain([origin[1], m[1]].map(y.invert).sort())); | |
| } | |
| 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); | |
| function refresh() { | |
| svg.select(".x.axis").call(xAxis); | |
| svg.select(".y.axis").call(yAxis); | |
| } | |
| </script> |
usamec
commented
Jun 10, 2014
noscript
commented
Jun 1, 2015
@usamec Thanks for the fix!
akshayKhot
commented
Jun 7, 2016
•
Thank you for this example.
What does the following code means?
.on("mousemove.zoomRect", function() {
I am not aware of this syntax. I tried the docs but not sure what to look for. zoomRect is a boolean variable, what does it mean when we say mousemove.zoomRect ?
Any help is appreciated. Thanks.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This line is wrong:
when the input domain is something like (90, 100), it gets sorted as (100, 90) (js sort arrays as strings).
Should be:
(and the same for y axis).