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)
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