1

I'm rendering something using d3, and using d3.zoom to zoom with the mouse. It works, but I also need to change the contents of the svg periodically (doing a full $('svg').html($('svg').html()) call, because my SVG contains HTML).

The problem is that when I do this, I lose the zoom state. I've tried to keep track of it by storing the zoom transform and reapplying it after the update, but it's not working.

Here's a pen with my attempt, using the dagre-d3 example diagram.

Here's what I'm doing, in short:

let transform;

function dag(){
  // ... set up stuff here

  var svg = d3.select("svg"),
      inner = svg.select("g");

  // Set up zoom support
  var zoom = d3.zoom().on("zoom", function() {
        //store transform
        transform = d3.event.transform
        inner.attr("transform", d3.event.transform);
      });
  svg.call(zoom);

  // render graph
  new dagreD3.render()(inner, g);

  // Center the graph
  var initialScale = 0.75;
  svg.call(zoom.transform, d3.zoomIdentity.translate((svg.attr("width") - g.graph().width * initialScale) / 2, 20).scale(initialScale));

  svg.attr('height', g.graph().height * initialScale + 40);
}

setInterval(() => {
  // simulate reloading content
  dag()
  console.log('render again')
  //try and apply saved transform state
  transform && inner.attr("transform", transform);
}, 2000)
user31415629
  • 925
  • 6
  • 25
  • Is that what you wanted? https://codepen.io/anon/pen/MBwBYY?editors=0011 (it still centres everytime you draw on the interval, but I'm guessing you want it like that?) If it is what you wanted, it's because you were re-scaling with the default 'initialScale', you needed to update that. Check line 63. – REEE Jul 12 '18 at 17:56
  • @REEE I don't want it to center, but removing that code sorts it out. Thanks for your help, can you write up an answer so I can accept it? – user31415629 Jul 13 '18 at 08:45
  • Awesome, glad I could help. I wrote the answer with a bit more of an explanation just in case anyone else encounters a similar problem. Good luck with the rest of your project :). – REEE Jul 13 '18 at 09:16

1 Answers1

2

The svg scale is set again using the 'initialScale' variable after the transform is set in the zoom func, the problem can be seen here:

  // Center the graph
  var initialScale = 0.75;
  svg.call(zoom.transform, d3.zoomIdentity.translate((svg.attr("width") - g.graph().width * initialScale) / 2, 20).scale(initialScale));

To get around this you need to change the value in the 'initialScale' variable based on the current scale transform of the svg. As seen here:

  // Center the graph
  var initialScale = (typeof transform != 'undefined') ? transform.k : 0.75;
  svg.call(zoom.transform, d3.zoomIdentity.translate((svg.attr("width") - g.graph().width * initialScale) / 2, 20).scale(initialScale));

A codepen of the code with the fix: https://codepen.io/anon/pen/MBwBYY?editors=0011

REEE
  • 517
  • 2
  • 11