I have an <svg>
whose width is 100% of its container. When the container is resized, I update the linear xScale.range()
to represent the new resized width of the <svg>
. Apparently, I then need to reapply the range to my zoom behaviour as outlined in the zoom.x() documentation:
If the scale's domain or range is modified programmatically, this function should be called again. Setting the x-scale also resets the scale to 1 and the translate to [0, 0].
The resetting of the scale
and translate
is where I have a problem. If I have previously zoomed in before resizing, I call zoom.x( xScale )
and now d3 thinks the chart's scale is 1 and translate is 0,0 therefore, I cannot zoom out or pan.
Is my approach to the way I handle the resize incorrect?
It looks like the best strategy is to cache the scale and translate values, reset, then reapply. For the record, this code (within my resize handler) roughly shows my solution:
// Cache scale
var cacheScale = zoom.scale();
// Cache translate
var cacheTranslate = zoom.translate();
// Cache translate values as percentages/ratio of the full width
var cacheTranslatePerc = zoom.translate().map( function( v, i, a )
{
return (v * -1) / getFullWidth();
} );
// Manually reset the zoom
zoom.scale( 1 ).translate( [0, 0] );
// Update range values based on resized container dimensions
xScale.range( [0, myResizedContainerWidth] );
// Apply the updated xScale to the zoom
zoom.x( xScale );
// Revert the scale back to our cached value
zoom.scale( cacheScale );
// Overwrite the x value of cacheTranslate based on our cached percentage
cacheTranslate[0] = -(getFullWidth() * cacheTranslatePerc[0]);
// Finally apply the updated translate
zoom.translate( cacheTranslate );
function getFullWidth()
{
return xScale.range()[1] * zoom.scale();
}
For those that stumbled upon this looking for a v4 solution, using the awesome setup from Philip above that is for v3, I adapted a loosely-based v4 solution. I spread out the variables to explain it the way v3 used to do it (since it makes more sense in v3). v4 does not have the ability to force the X value like v3 did, so you have to calculate out the existing X and then divide by the scale (K). (There may be a better way to do the final calculation + setting it on the zoom, but the d3-zoom documentation is a little confusing on this)
let transform = d3.zoomTransform(node);
let oldFullWidth = (oldWidth * transform.k);
let newFullWidth = (newWidth * transform.k);
// this is the result you want X to be
let newX = -(newFullWidth * ((transform.x * -1) / oldFullWidth));
// this is just deducting from the existing so you can call .translate
let translateBy = (newX - transform.x) / transform.k;
d3.select(node).call(myZoom.transform, transform.translate(translateBy, 0));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With