Skip to content

Instantly share code, notes, and snippets.

@jazeee
Forked from jgbos/README.md
Last active May 26, 2016 20:09
Show Gist options
  • Save jazeee/5414435213db1bee9e912a9dca83a41f to your computer and use it in GitHub Desktop.
Save jazeee/5414435213db1bee9e912a9dca83a41f to your computer and use it in GitHub Desktop.
Simple Independent Axis Zooming and Panning for x, y, or xy
This is the title of the gist (It is first, since it starts with a space in the name)
license: mit

Independent Axis Zooming and Panning on x, y, or xy

This is an example of adding simple interface for independent axis and single axis zoom and pan capability using the mouse. When the mouse is over the x or y axes, the panning and zooming with the mouse will execute on only those axes. When the mouse is over the main plotting canvas, pan and zoom with the mouse works on both axes.

Each zoom behavior is reset after redraw, which is how this works. The code applies the zoom behavior on each scale, and then redraws and recreates the zoom behaviors.

Feel free to comment on the gist. Also see: http://bl.ocks.org/jazeee/5414435213db1bee9e912a9dca83a41f

Credit to @jgbos for this simple but useful solution. (Forked from https://gist.github.com/jgbos/9752277)

<!DOCTYPE html>
<meta charset="utf-8">
<title>Simple Independent Axis Zooms on x, y, or xy</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
<body>
<div id="chart"></div>
<script>
var exampleChart = example();
var data = [];
for (var i = 0; i < 100; i++) {
data.push([Math.random(), Math.random()]);
}
d3.select('#chart')
.append("svg").attr("width", window.innerWidth).attr("height",window.innerHeight)
.datum(data).call(exampleChart);
function example() {
var svg;
var margin = {
top: 60,
bottom: 80,
left: 60,
right: 0
};
var width = 500;
var height = 400;
var xaxis = d3.svg.axis();
var yaxis = d3.svg.axis();
var xscale = d3.scale.linear();
var yscale = d3.scale.linear();
var xyzoom, xzoom, yzoom;
updateZooms();
function chart(selection) {
selection.each(function(data) {
svg = d3.select(this).selectAll('svg').data([data]);
svg.enter().append('svg');
var g = svg.append('g')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
g.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom);
g.append("svg:rect")
.attr("class", "border")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom)
.style("stroke", "black")
.style("fill", "none");
g.append("g").attr("class", "x axis")
.attr("transform", "translate(" + 0 + "," + (height - margin.top - margin.bottom) + ")");
g.append("g").attr("class", "y axis");
g.append("g")
.attr("class", "scatter")
.attr("clip-path", "url(#clip)");
g
.append("svg:rect")
.attr("class", "zoom xy box")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom)
.style("visibility", "hidden")
.attr("pointer-events", "all")
.call(xyzoom);
g
.append("svg:rect")
.attr("class", "zoom x box")
.attr("width", width - margin.left - margin.right)
.attr("height", height - margin.top - margin.bottom)
.attr("transform", "translate(" + 0 + "," + (height - margin.top - margin.bottom) + ")")
.style("visibility", "hidden")
.attr("pointer-events", "all")
.call(xzoom);
g
.append("svg:rect")
.attr("class", "zoom y box")
.attr("width", margin.left)
.attr("height", height - margin.top - margin.bottom)
.attr("transform", "translate(" + -margin.left + "," + 0 + ")")
.style("visibility", "hidden")
.attr("pointer-events", "all")
.call(yzoom);
// Update the x-axis
xscale.domain(d3.extent(data, function(d) {
return d[0];
}))
.range([0, width - margin.left - margin.right]);
xaxis.scale(xscale)
.orient('bottom')
.tickPadding(10);
svg.select('g.x.axis').call(xaxis);
// Update the y-scale.
yscale.domain(d3.extent(data, function(d) {
return d[1];
}))
.range([height - margin.top - margin.bottom, 0]);
yaxis.scale(yscale)
.orient('left')
.tickPadding(10);
svg.select('g.y.axis').call(yaxis);
draw();
});
return chart;
}
function update() {
var gs = svg.select("g.scatter");
var circle = gs.selectAll("circle")
.data(function(d) {
return d;
});
circle.enter().append("svg:circle")
.attr("class", "points")
.style("fill", "steelblue")
.attr("cx", function(d) {
return X(d);
})
.attr("cy", function(d) {
return Y(d);
})
.attr("r", 4);
circle.attr("cx", function(d) {
return X(d);
})
.attr("cy", function(d) {
return Y(d);
});
circle.exit().remove();
}
function updateZooms() {
xyzoom = d3.behavior.zoom()
.x(xscale)
.y(yscale)
.on("zoom", draw);
xzoom = d3.behavior.zoom()
.x(xscale)
.on("zoom", draw);
yzoom = d3.behavior.zoom()
.y(yscale)
.on("zoom", draw);
}
function draw() {
svg.select('g.x.axis').call(xaxis);
svg.select('g.y.axis').call(yaxis);
update();
// After every draw, we reinitialize zoom. After every zoom, we reexecute draw, which will reinitialize zoom.
// This is how we can apply multiple independent zoom behaviors to the scales.
// (Note that the zoom behaviors will always end up with zoom at around 1.0, and translate at around [0,0])
updateZooms();
svg.select('rect.zoom.xy.box').call(xyzoom);
svg.select('rect.zoom.x.box').call(xzoom);
svg.select('rect.zoom.y.box').call(yzoom);
};
// X value to scale
function X(d) {
return xscale(d[0]);
}
// Y value to scale
function Y(d) {
return yscale(d[1]);
}
return chart;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment