Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3.js pan and zoom jumps when using mouse after programatic zoom

I'm trying to center my d3 graph when it loads. So I run

var g = svg.append("g");

//... then later

g.call(zoom.transform, center);

It's not actually centering, it's just scaling right now. But the scale works. The problem is, when I then scroll to zoom in or out, it starts from 1 instead of 0.5. So it jumps.

Here's my jsbin.

like image 738
Tyler Graf Avatar asked Feb 07 '23 12:02

Tyler Graf


1 Answers

You are applying your event on a different object (the g) then what the zoom is set up on (the svg). Since the zoom event is referenced against the element it is applied to, d3 does not now about your initial centering. From the docs:

The zoom behavior stores the zoom state on the element to which the zoom behavior was applied, not on the zoom behavior itself. This is because the zoom behavior can be applied to many elements simultaneously, and each element can be zoomed independently. The zoom state can change either on user interaction or programmatically via zoom.transform.

Changing your call to svg.call(zoom.transform, center); fixes your issue.

Complete code:

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    transform = d3.zoomIdentity;

var points = d3.range(2000).map(phyllotaxis(10));

var zoom = d3.zoom()
    .scaleExtent([1 / 2, 8])
    .on("zoom", zoomed);

var g = svg.append("g");

g.selectAll("circle")
    .data(points)
  .enter().append("circle")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .attr("r", 2.5)
    .call(d3.drag()
        .on("drag", dragged));

svg.call(zoom);

function zoomed() {
  g.attr("transform", d3.event.transform);
}

function center() {
  return d3.zoomIdentity
        .scale(0.5);
}

function dragged(d) {
  d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}

function phyllotaxis(radius) {
  var theta = Math.PI * (3 - Math.sqrt(5));
  return function(i) {
    var r = radius * Math.sqrt(i), a = theta * i;
    return {
      x: width / 2 + r * Math.cos(a),
      y: height / 2 + r * Math.sin(a)
    };
  };
}

svg.call(zoom.transform, center);
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
</body>
</html>
like image 150
Mark Avatar answered Feb 10 '23 23:02

Mark