Skip to content

Instantly share code, notes, and snippets.

@jgbos
Last active February 25, 2018 20:23
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save jgbos/9752277 to your computer and use it in GitHub Desktop.
Simple Single Axis Zoom

This is an example of adding simple interface for 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. Feel free to comment on the gist.

<!DOCTYPE html>
<meta charset="utf-8">
<title>Simpe Single Axis Zoom</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 ex_chart = example().zoom(true);
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(ex_chart);
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 zoomable = true;
var xyzoom = d3.behavior.zoom()
.x(xscale)
.y(yscale)
.on("zoom", zoomable ? draw : null);
var xzoom = d3.behavior.zoom()
.x(xscale)
.on("zoom", zoomable ? draw : null);
var yzoom = d3.behavior.zoom()
.y(yscale)
.on("zoom", zoomable ? draw : null);
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 zoom_update() {
xyzoom = d3.behavior.zoom()
.x(xscale)
.y(yscale)
.on("zoom", zoomable ? draw : null);
xzoom = d3.behavior.zoom()
.x(xscale)
.on("zoom", zoomable ? draw : null);
yzoom = d3.behavior.zoom()
.y(yscale)
.on("zoom", zoomable ? draw : null);
svg.select('rect.zoom.xy.box').call(xyzoom);
svg.select('rect.zoom.x.box').call(xzoom);
svg.select('rect.zoom.y.box').call(yzoom);
}
function draw() {
svg.select('g.x.axis').call(xaxis);
svg.select('g.y.axis').call(yaxis);
update();
zoom_update();
};
// X value to scale
function X(d) {
return xscale(d[0]);
}
// Y value to scale
function Y(d) {
return yscale(d[1]);
}
chart.zoom = function (_){
if (!arguments.length) return zoomable;
zoomable = _;
return chart;
}
return chart;
}
</script>
@jazeee
Copy link

jazeee commented May 26, 2016

Thanks for this. It really helped me with a project I am working on.
(Note also, a small typo in the html <title> tag: Simpe should be Simple)

@vertighel
Copy link

Thank you for the example.
How is this achievable in D3 4.0?

@tnunamak
Copy link

@vertighel, have a look at this v4 zooming example, but instead of

  view.attr("transform", d3.event.transform);

try something like

   view.attr('transform', `scale(${d3.event.transform.k}, 1)`);

The trick is that the transform uses a single scale k for both x and y, so you need to force the y scale to stay 1 with a custom transform. Note that the transform I added doesn't include translation, which you'd need to add for panning.

@brunostg
Copy link

Great exemple : can you make the same in v4 ?

@tayabsoomro
Copy link

Great example! I wanna make it so that the x-axis is not scalable but only scrollable (i.e one should be able to click and drag it left and right but not change the x-scale of it), how would I go about doing that? I have tried almost everything so far.

@sandwichsudo
Copy link

Great example, many thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment