anvaka / panzoom

Universal pan and zoom library (DOM, SVG, Custom)
https://anvaka.github.io/panzoom/demo/attach-via-script.html
MIT License
1.82k stars 293 forks source link

feature request: zoom to fit #48

Open cdaringe opened 6 years ago

cdaringe commented 6 years ago

greetings. thx for the great lib. i see that you offer .zoomAbs(...), but my content is unknown ahead of time. how would you feel about offering a .fit() method that zoomed in/out to fit the panzoom content to the content container?

thanks!

cjinghong commented 6 years ago

That would be really helpful. Is there a way to pan the scene to a specific element?

rbonomo commented 5 years ago

It's not in the README.md, but this library comes with two methods that may help you: smoothZoom and zoomTo. ctrl-F the method names in the src js to find usage.

willsza commented 5 years ago

Hello gentlemen! Has anyone made any progress in implementing FIT?

retzloff commented 5 years ago

Can someone briefly explain what clientX and clientY are in publicZoomTo? I followed it in the source as @rbonomo suggested. zoomTo (aliased to publicZoomTo) => zoomByRatio => transformToScreen at which point, I have a trouble understanding what's happening. Are those relative to the current x, y or the origin point? Also, what is the origin - top left? Center? Somewhere else?

Thank you in advance.

ystreibel commented 5 years ago

To create a workaround for zoom to fit feature, I tried to use :

pz.centerOn(myelement);
pz.smoothZoom(window.innerWidth / 2 , window.innerHeight / 2, fit_computed_ratio);

It doesn't work because the smoothZoom need to wait the end of the centerOn animation.

marcj commented 4 years ago

Here is a implementation that actually works: 2020-02-12 17 34 01

It has a toggle option from zoom-to-fit back to 100% and vice-versa, but you comment that out easily.

            const svg = YOUR_SVG_ELEMENT HERE;

            const parent = svg.viewportElement!;
            const rectParent = parent.getBoundingClientRect();
            const rectScene = svg.getBoundingClientRect();

            const xys = this.graphPanZoom.getTransform();
            const originWidth = rectScene.width / xys.scale;
            const originHeight = rectScene.height / xys.scale;
            const zoomX = (rectParent.width - 20) / originWidth;
            const zoomY = (rectParent.height - 20) / originHeight;

            let targetScale = zoomX < zoomY ? zoomX : zoomY;

            //when target scale is the same as currently, we reset back to 100%, so it acts as toggle.
            if (Math.abs(targetScale - xys.scale) < 0.005) {
                //reset to 100%
                targetScale = 1;
            }

            const targetWidth = originWidth * xys.scale;
            const targetHeight = originHeight * xys.scale;
            const newX = targetWidth > rectParent.width ? -(targetWidth / 2) + rectParent.width / 2 : (rectParent.width / 2) - (targetWidth / 2);
            const newY = targetHeight > rectParent.height ? -(targetHeight / 2) + rectParent.height / 2 : (rectParent.height / 2) - (targetHeight / 2);

            //we need to cancel current running animations
            this.graphPanZoom.pause();
            this.graphPanZoom.resume();

            const xDiff = Math.abs(newX - xys.x);
            const yDiff = Math.abs(newX - xys.x);
            if (xDiff > 5 || yDiff > 5) {
                //overything over 5px change will be animated
                this.graphPanZoom.moveBy(
                    newX - xys.x,
                    newY - xys.y,
                    true
                );
                await sleep(0.25);
            } else {
                this.graphPanZoom.moveBy(
                    newX - xys.x,
                    newY - xys.y,
                    false
                );
            }

            //correct way to zoom with center of graph as origin when scaled
            this.graphPanZoom.smoothZoomAbs(
                xys.x + originWidth * xys.scale / 2,
                xys.y + originHeight * xys.scale / 2,
                targetScale,
            );

Typescript tho.

ystreibel commented 4 years ago

hi @marcj 👋🏻,

In your case, if your svg element has a previous animation with panzoom before your running code, the svg.getBoundingClientRect(); values could be wrong.

marcj commented 4 years ago

@ystreibel Yes, that's why it's normalized using const originWidth = rectScene.width / xys.scale and that current animation is stopped.

ystreibel commented 4 years ago

@marcj, I tried your code in mine and current animation isn't stopped. If you want to reproduce do

 this.graphPanZoom = panzoom(inkSvg, { autocenter: true, bounds: true });

just before

const svg = YOUR_SVG_ELEMENT HERE;

in window load event listener

function loaded {
 HERE THE CODE
}
window.addEventListener("load", loaded, true);
marcj commented 4 years ago

autocenter: true doesn't use any animations, so I don't know where your previous animation is coming from that isn't stopped via panZoom.pause(). Of you use css transitions, then you should probably remove them.

ystreibel commented 4 years ago

No animations right but transform is processing via autocenter() call.

I try to made this codepen to explain the fact .

Feel free to contribute.

mruac commented 1 year ago

try this maybe?

const container = document.querySelector('div');
panzoom.showRectangle(container.getBoundingClientRect());
panzoom.moveBy(0,0); //required to set the css transforms, as the last command only sets it internally.