# Reset the zoom state dynamically?

**URL:** <https://talk.observablehq.com/t/reset-the-zoom-state-dynamically/8829>\
**Category:** Help\
**Created:** [February 20, 2024, 4:55am UTC](https://talk.observablehq.com/t/reset-the-zoom-state-dynamically/8829 "2024-02-20T04:55:24Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![cpill](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/cpill/32/8519_2.png) [@cpill](https://talk.observablehq.com/u/cpill)\
**Post date:** [February 20, 2024, 4:55am UTC](https://talk.observablehq.com/t/reset-the-zoom-state-dynamically/8829/1 "2024-02-20T04:55:24Z")

</div>

So, I have various groups of SVG elements, wrapped in s and want the user to be able to drag around and resize these objects when they mouse over them.

I kind of got it going but when switching between gourps, by onMouseOut of one and onMouseOver of another there is weird stuff going on. The zoom state of the last group is carried over to the new target group and weirdness happens. The docs state:

> The zoom behavior stores the zoom state on the element to which the zoom behavior was applied, not on the zoom behavior itself. This allows the zoom behavior to be applied to many elements simultaneously with independent zooming. The zoom state can change either on user interaction or programmatically via [_zoom_.transform](https://d3js.org/d3-zoom#zoom_transform).

I figure what is going on is that the state is stored in the parent i.e. svg element usually, and the transform is applied to a sub element, using the parent relatively that the transform is calculated against.

some code

```auto
const svg = d3.select(svgRef.current); // frame of reference
const g = svg.select("g");// should be the main group
function defaultZoomed({ transform }) {
    g.attr("transform", transform);
}
const zoomedFunc = zoomed ? zoomed : defaultZoomed;

// Reset to kill any previoud state
svg.on(".zoom", null);
// Create a new zoom behavior
svg.call(d3.zoom()
    .extent([[0, 0], [dimensions.width, dimensions.height]])
    .scaleExtent([0.01, 10])
    .on("zoom", zoomedFunc));

```

Here `zoomed` is set `onMouseOver` and `onMouseOut` of the various zoomable things (I’m doing all this in React to keep it interesting).

What I really need to do is be able to **reset the zoom state** which I assume is kept on the parent `svg` element, but how?

---

<div class="post-metadata">

**Author:** ![eagereyes](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/eagereyes/32/6255_2.png) [@eagereyes](https://talk.observablehq.com/u/eagereyes)\
**Post date:** [February 21, 2024, 12:46am UTC](https://talk.observablehq.com/t/reset-the-zoom-state-dynamically/8829/2 "2024-02-21T00:46:33Z")

</div>

This is tricky to answer without seeing more code. First, I’d challenge your assumption that the zoom state is stored in the parent node, I don’t think that’s the case. You also seem to be setting values by hand, rather than using transitions. That might explain how the state seems to propagate from one group to another. If you set this up using a join and transitions, plus a key function to create a binding between the data and SVG elements, it’s likely to work better.

---

<div class="post-metadata">

**Author:** ![cpill](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/cpill/32/8519_2.png) [@cpill](https://talk.observablehq.com/u/cpill)\
**Post date:** [February 21, 2024, 9:02am UTC](https://talk.observablehq.com/t/reset-the-zoom-state-dynamically/8829/3 "2024-02-21T09:02:45Z")

</div>

Hi @eagereyes thanks for your response,

I guess what I’m trying to achieve is very similar to [this video](https://www.youtube.com/watch?v=mIVJZctCyqg). Why i think the state is on the parent is if you look at the object getting zoomed you’ll see that the only change on the object is the transform attribute nothing changes on the SVG parent. If the state were to be kept on the object getting zoomed then why would there be this jerkiness when the target is switched. It’s implied by 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.

You can see in the code the zoom object is applied to the parent SVG object

```auto
svg.call(d3.zoom().on("zoom", zoomedFunc));

```

`.call` takes the pasted function, here `d3.zoom`, and passes it’s parent into it, here `svg`. The transition is calculated relative to `svg` and then handed to the “zoom” event function (which I’m switching dynamically based on mouse events).

I don’t understand what you mean about “setting values by hand”, what values? Perhaps you could give an example or point me in the direction of one when you say use join and transitions?

Here is the full code:

```auto
function Space() {
    const defaultZoomed = () => ({ transform }) => d3.select("g#MAIN").attr("transform", { transform });
    const [activeZoomed, setActiveZoomed] = useState(defaultZoomed);

    return <Zoomable setZoomed={setActiveZoomed} key={e.contentUrl}>
        <Something />
    </Zoomable>;
}

const Zoomable = ({ svgRef, setZoomed, children }) => {
    const ref = useRef(null);
    // const [hovered, eventHandlers] = useHover(ref);

    const zoomed = () => ({ transform }) => {
        const g = d3.select(ref.current)
        g.attr("transform", transform);
    }

    const onMouseOver = () => {
        setZoomed(zoomed);
    }

    const onMouseOut = () => {
        setZoomed(null);
    }

    useEffect(() => {
        setZoomed(zoomed);
    }, [hovered])

    return (
        <g
            className="draggable"
            ref={ref}
            onMouseOver={onMouseOver}
            onMouseOut={onMouseOut}
        >
            {children}
        </g >
    );
};

function useZoomableSVG(svgRef, zoomed) {

    useEffect(() => {
        const svg = d3.select(svgRef.current); // frame of reference
        const g = svg.select("g");// should be the main group
        function defaultZoomed({ transform }) {
            g.attr("transform", transform);
        }
        const zoomedFunc = zoomed ? zoomed : defaultZoomed;

        // Reset to kill any previoud state
        svg.on(".zoom", null);
        // Create a new zoom behavior
        svg.call(d3.zoom()
            .extent([[0, 0], [dimensions.width, dimensions.height]])
            .scaleExtent([0.01, 10])
            .on("zoom", zoomedFunc));

    }, [zoomed])
};

```

The use useZoomableSVG is a simple variant on [this](https://observablehq.com/@d3/zoom-svg-rescaled?collection=@d3/d3-zoom) example

---

<div class="post-metadata">

**Author:** ![cpill](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/cpill/32/8519_2.png) [@cpill](https://talk.observablehq.com/u/cpill)\
**Post date:** [February 21, 2024, 8:46pm UTC](https://talk.observablehq.com/t/reset-the-zoom-state-dynamically/8829/4 "2024-02-21T20:46:59Z")

</div>

So looking at [the code](https://github.com/d3/d3-zoom/blob/main/src/zoom.js#L87) it seems to keep the state on a `.__zoom` property of the selection. I’ll try killing that when switching the `zoomed` function.

---

<div class="post-metadata">

**Author:** ![cpill](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/cpill/32/8519_2.png) [@cpill](https://talk.observablehq.com/u/cpill)\
**Post date:** [February 22, 2024, 5:49am UTC](https://talk.observablehq.com/t/reset-the-zoom-state-dynamically/8829/5 "2024-02-22T05:49:49Z")

</div>

ok, so somebody has been doing category theory and so I need to set the `.__zoom` property to the Transform Identity object which I have no idea how to get at…? Transform is fairly small so I guess I’ll just copy the whole class file and keep my finger crossed that it doesn’t change…

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [February 22, 2024, 1:14pm UTC](https://talk.observablehq.com/t/reset-the-zoom-state-dynamically/8829/6 "2024-02-22T13:14:42Z")

</div>

This might help (ignore the part about removing the handlers):

> [@update value of another input when an input is triggered in a multi input single object](https://talk.observablehq.com/t/update-value-of-another-input-when-an-input-is-triggered-in-a-multi-input-single-object/8532/8):
>
> Be sure to take a look at the [d3-zoom docs](https://d3js.org/d3-zoom#zoom_transform). There you’ll find an example to reset a zoom transform, which you need to apply before you remove the handlers so that your SVG can still update: } else { svgOrigin.call(zoom.transform, d3.zoomIdentity); // Remove all \*.zoom event handlers svgOrigin.on(".zoom", null); }
