chrvadala / react-svg-pan-zoom

:eyes: A React component that adds pan and zoom features to SVG
https://chrvadala.github.io/react-svg-pan-zoom/
MIT License
684 stars 126 forks source link

Make viewer stay inside SVG boundaries? #143

Open enekesabel opened 5 years ago

enekesabel commented 5 years ago

Hi,

Is it possible to restrict the viewer going outside the boundaries of the SVG element? I'd like to achieve a map-like behavior where you can navigate only inside the svg map. I thought preventPanOutside will do exactly that, but it lets the user drag the map till the corners of the viewer.

Could somebody point me in the right direction?

Thanks, Abel

krnlde commented 5 years ago

Without having any context how this lib actually does it: the preventPanOutside calculation seems to use the opposite corners. The calculation could be "corrected" to match your request if the left corner adds the SVGs width, the right corner substracts the width, top adds the height and bottom substracts it - but it's probably easier to just exchange sides (left<-> right, top <->bottom). I'm not sure how @chrvadala calculates the boundaries but you/he could use the corresponding values provided by .getBoundingClientRect() / .getBBox() with the applied .getScreenCTM().inverse() / .getCTM().inverse() to do that.

wolasss commented 4 years ago

I agree and I also need this feature, if I find some time I might implement it and create a PR

davidsharp commented 4 years ago

It's a bit of a hack, but you catch pan events with onPan and limit the values to within the SVG and passing your new doctored values to setState. In the values object, e refers to x-axis translation, and f refers to y-axis translation. It's worth noting that the translations describes the movement of the SVG, so by panning an SVG up (scrolling down the 'page'), f goes from 0 into negative digits.

I'm using it here to disable left/right pan (I've got the SVG width set to fill the viewer), then by working out the scaling factor, I can get the height of the scaled SVG (and then I take away the viewer height as the number basically refers to the top of the viewer window):

onPan={value=>{
    //calculate scaled SVG height minus a single viewer height
    const scaledMaxHeight = (value.SVGHeight/(value.SVGWidth/value.viewerWidth))-value.viewerHeight
    this.setState({value:{
        ...value,
        // prevents panning left/right
        e:0,
        // limit up/down panning to within the SVG
        f: value.f>0?0
            :value.f<0-scaledMaxHeight?0-scaledMaxHeight
            :value.f
        }})
}}
WouterrV commented 1 year ago

I started building this feature: https://codesandbox.io/s/react-svg-pan-zoom-testing-wm6ssl (and abandoned it, because it's not really a must-have for our app)

But intercepting the value in onPan gets quite janky because the UI gets adjusted after the fact. What would probably work very fluidly is using a custom setValue function that corrects out-of-bounds values.

etienne-85 commented 1 year ago

I'm also looking forward to that feat. As it might be a must have for our app, may have a look if no one else has implemented it and if not requiring to much time to implement . Will let you know if I have a working POC

EDIT: Think I've got basic implementation by listening to value changes and restricting depending on image displayed size This is still of an hack for now with some values tweaked on the fly but to give an idea, relevant piece of code I used:

    // image displayed size (after zoom scale applied)
    const onScreenWidth = SVGWidth * widthScale;
    const onScreenHeight = SVGHeight * heightScale;
    // compute max offsets
    const maxWidthOffset = viewerWidth - onScreenWidth + 5;
    const maxHeightOffset = viewerHeight - onScreenHeight + 5;
    // adjusting current pan values to match offset boundaries
    widthOffset = Math.min(0, Math.max(widthOffset, maxWidthOffset));
    heightOffset = Math.min(0, Math.max(heightOffset, maxHeightOffset));

Link to working POC which may be updated later on: https://l4p4qq.csb.app/