Skip to content

Instantly share code, notes, and snippets.

@mrcslws
Last active March 17, 2017 23:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrcslws/387a619d570508451ce2e7209ffaac7d to your computer and use it in GitHub Desktop.
Save mrcslws/387a619d570508451ce2e7209ffaac7d to your computer and use it in GitHub Desktop.
d3-zoom: You must position your rect with "transform"
license: gpl-3.0

This demonstrates that d3-zoom doesn't compute the zoom point as expected if you position a zoom-catching rect with "x" and "y" attributes. If you position the rect with "transform", it works as expected.

Point your mouse at a certain point, e.g. (100, 100). Try to zoom in on it. See how the point (100, 100) starts to scroll out of sight?

Now click "Fix it" and try it again. It works.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis path {
display: none;
}
.axis line {
stroke-opacity: 0.3;
shape-rendering: crispEdges;
}
.view {
fill: url(#gradient);
stroke: #000;
}
button {
position: absolute;
top: 20px;
left: 20px;
}
</style>
<button>Fix it</button>
<svg width="960" height="500">
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0.0%" stop-color="#2c7bb6"></stop>
<stop offset="12.5%" stop-color="#00a6ca"></stop>
<stop offset="25.0%" stop-color="#00ccbc"></stop>
<stop offset="37.5%" stop-color="#90eb9d"></stop>
<stop offset="50.0%" stop-color="#ffff8c"></stop>
<stop offset="62.5%" stop-color="#f9d057"></stop>
<stop offset="75.0%" stop-color="#f29e2e"></stop>
<stop offset="87.5%" stop-color="#e76818"></stop>
<stop offset="100.0%" stop-color="#d7191c"></stop>
</linearGradient>
</defs>
</svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var padding = {top: 100, right: 100, bottom: 100, left: 100};
var svg = d3.select("svg"),
width = +svg.attr("width") - padding.top - padding.bottom,
height = +svg.attr("height") - padding.left - padding.right;
svg.append("defs")
.append('clipPath')
.attr('id', "myClipPath")
.append('rect')
.attr('width', width)
.attr('height', height);
var zoom = d3.zoom()
.extent([[0, 0], [width, height]])
.scaleExtent([1, 40])
.translateExtent([[0, 0], [width, height]])
.on("zoom", zoomed);
var x = d3.scaleLinear()
.domain([0, width])
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, height])
.range([0, height]);
var xAxis = d3.axisBottom(x)
.tickSize(height)
.tickPadding(8 - height);
var yAxis = d3.axisRight(y)
.ticks(4)
.tickSize(width)
.tickPadding(8 - width);
var view = svg
.append("g")
.attr("transform", "translate(" + padding.left + "," + padding.top + ")")
.style("clip-path", "url(#myClipPath)")
.append("rect")
.attr("class", "view")
.attr("width", width)
.attr("height", height);
var gX = svg.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(" + padding.left + "," + padding.top + ")")
.call(xAxis);
var gY = svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
d3.select("button")
.on("click", fixIt);
var zoomCatcher = svg
.append("rect")
.attr("class", "zoomCatcher")
.attr("x", padding.left)
.attr("y", padding.top)
.attr("width", width)
.attr("height", height)
.attr("fill", "transparent")
.attr("stroke", "none")
.call(zoom);
function zoomed() {
view.attr("transform", d3.event.transform);
gX.call(xAxis.scale(d3.event.transform.rescaleX(x)));
gY.call(yAxis.scale(d3.event.transform.rescaleY(y)));
}
function fixIt() {
zoomCatcher
.call(zoom.transform, d3.zoomIdentity);
zoomCatcher
.attr("transform", "translate(" + padding.left + "," + padding.top + ")")
.attr("x", 0)
.attr("y", 0);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment