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.
Last active
October 6, 2018 08:54
-
-
Save jasondavies/3689931 to your computer and use it in GitHub Desktop.
Zoom by Rectangle
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> | |
<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> |
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
Copyright 2012 Jason Davies https://www.jasondavies.com | |
Permission to use, copy, modify, and/or distribute this software for any | |
purpose with or without fee is hereby granted, provided that the above | |
copyright notice and this permission notice appear in all copies. | |
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | |
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | |
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | |
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | |
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
PERFORMANCE OF THIS SOFTWARE. |
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.
@akshayKhot,
the following command will add an event listener mousemove with a naming/tag zoomRect which is a single string used to differentiate other mousemove events. We use it here to unsubscribe it at the mouseup event !
you can change the zoomRect with foo as long as you are consistent with the unsubscribe call.
.on("mousemove.foo", function() { ... }) // add event listener
.on("mousemove.foo", null) // remove event listener
with regards,
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@usamec Thanks for the fix!