I am currently trying to make a simple graph using D3 and React, where it is possible to zoom in/out on both axes, or on one of the two axes separately. An example of such a graph made only with D3 is available here (http://bl.ocks.org/jgbos/9752277).
I managed to get the same behaviour using D3 and React, as you can see in this sandbox (https://codesandbox.io/s/objective-cohen-b92pr). This example works almost perfectly, but I sometimes see a "jump" when, for example, I zoom in on Y, then X and then both (see gif below).

My guess is that d3 stores a zoomTransform object {k, x, y} for each container (x-axis-listener, y-axis-listener & svg) on which the zoom event is called. I suppose we should be able to share the same instance of the object between all the containers, but so far I have not been successful.
Any guesses?
Thanks a lot !
There is a good reason why you can either have X and Y zoom separately, or have a single zoom factor in your graph (which applies to both X and Y), but you cannot have a situation where you can smoothly change them all. That's why:
A D3 zoom identity object has 3 attributes: x, y, k, where x and y are offsets from the initial pos (0,0) and k is the current zoom factor (1 by default).
Now, if you want to manage 3 separate zoom identities, you have actually 3 different objects {xV, yV, kV} for vertical zoom, {xH, yH, kH} for horizontal and {xG, yG, kG} for the global zoom. When you rescale, you can apply xV to xH, yH to yG and so on, but you cannot apply two different values (kV and kH) to one (kG), you will need to choose between them (otherwise you have a logical contradiction here).
UPD: You can, however, replace zoomBoth callback with mouseWheel event handler, take X and Y zoom identities, change both of them proportionally (divide or multiply to the same number) and then apply changed zoom identities to both X and Y scales. It's possible :)
I finally came up with a working solution, which can be found here: https://codesandbox.io/s/quirky-yalow-3y6q4. This is mainly a refacto in React of this code observable: https://observablehq.com/@d3/x-y-zoom.
I'm using only one global transform object in the dom, and using the useState hook to store the global transform previous value, and one transform object for each axis.
The actual and the previous global transforms object allows me to retrieve the parameters to pass to the transform.translate and transform.scale methods in order to update the axis transforms object accordingly to where my mouse is.
This feels really "hacky" to me and I there should be a more elegant way of doing this.
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