pixi-viewport icon indicating copy to clipboard operation
pixi-viewport copied to clipboard

Restrict Panning and Zooming

Open krnbatta opened this issue 4 years ago • 12 comments

Hi Is there any way to restrict the panning to not make the canvas outside the view?

Basically, what I want is:

  • Not able to Zoom out.
  • Restrict Panning if the user tries to move the pixi canvas outside the view such that there is no empty space shown ever.
  • Resetting the zooming and panning to the original position.

Also, when I render a very large image, the initial coordinates are changed automatically. Is there any way that that does not happen?

krnbatta avatar Jan 23 '20 04:01 krnbatta

For restricting the panning, bounce seems to be working but it is only bouncing back if pushed towards right or bottom. The top and left side of the canvas is not restricted.

krnbatta avatar Jan 23 '20 05:01 krnbatta

ensureVisible and fit can be used to reset the zooming and panning to the original position. ensureVisible ensures panning is in the visible area. fit(false, width, height) ensures that zooming is as per width and height specified. So, I think it is pretty easy to reset the zooming and panning to original position.

krnbatta avatar Jan 23 '20 05:01 krnbatta

For restricting the panning, bounce seems to be working but it is only bouncing back if pushed towards right or bottom. The top and left side of the canvas is not restricted.

To ensure the view doesn't go out from the top and left, I wrote the following code:

viewport.on("drag-end", function(){
      if(this.getVisibleBounds().x > 0 || this.getVisibleBounds().x > 0){
        this.ensureVisible(0,0);
      }
    });

Is this correct way of doing this?

krnbatta avatar Jan 23 '20 05:01 krnbatta

This is the final code that I wrote to get closest to what I want:

    let viewport = new Viewport.Viewport({
      screenWidth: width,
      screenHeight: height,
      worldWidth: width*2,
      worldHeight: height*2,
      interaction: context.app.renderer.plugins.interaction,
      stopPropagation: true
    });
    viewport.drag().pinch().wheel().decelerate().bounce()
            .on("drag-end", function(){
              if(this.getVisibleBounds().x > 0 || this.getVisibleBounds().y > 0){
                this.ensureVisible(0,0);
              }
            })
            .on("zoomed", function(){
              if(this.lastViewport.scaleX < 1 || this.lastViewport.scaleY < 1){
                let self = this;
                this.ensureVisible(0,0);
                setTimeout(() => {
                  self.fit(false, width, height);
                }, 100);
              }
            });

Is this fine?

krnbatta avatar Jan 23 '20 06:01 krnbatta

Did you try .viewport.clamp()? That's designed to stop the viewport from panning beyond the boundaries (or whatever area you set).

davidfig avatar Jan 23 '20 09:01 davidfig

How does the clampZoom() minWidth and minHeight properties work? When I'm using renderer sizes 800x600, world size 10000x10000, clamp() properties: left: -10000, right: 10000, top: -10000, bottom: 10000.

And if i use clampZoom() maxWidth: 1600 maxHeight: 1200, i can zoom out twice the viewport size. But if i use minWidth: 400 and minHeight: 300 with my maxWidth and maxHeight properties, the whole renderer is empty somehow.

roitto avatar Feb 01 '20 14:02 roitto

Man, I need to update the docs. I keep forgetting how clampZoom works.

The minWidth/Height settings are how small the world can get (as it would appear on the screen) before clamping. The maxWidth/maxHeight is how larger the world can scale (as it would appear on the screen) before clamping.

For example, if you have a world size of 1000 x 1000 and a screen size of 100 x 100, if you set minWidth/Height = 100 then the world will not be able to zoom smaller than the screen size (ie, zooming out so it appears smaller than the screen). Similarly, if you set maxWidth/Height = 100 the world will not be able to zoom larger than the screen size (ie, zooming in so it appears larger than the screen).

Your renderer is likely empty because there is a conflict in the settings between the clamp and the clampZoom.

davidfig avatar Feb 01 '20 20:02 davidfig

Also struggle with this a little, but I'm probably not understanding how to use it.

With clamp, it would ideal to keep the world within view - as in, when the world is panned outside the left edge, the clamp would constrain to the right edge of the world.

image

Like, if clamp direction was "all", it would assure the world couldn't be pushed off the screen at any zoom level from any direction.

Often struggle with the origin / pivot point - centering, and zoom level percentages in regards to clamp and fitting the world.

jasonsturges avatar Feb 06 '20 06:02 jasonsturges

By default, clamp({ direction: all }) will clamp so the world can't leave the viewport.

If you want to allow the world to leave but never completely disappear, you'd have to reset the boundaries: clamp({ direction: all, left: -worldWidth * 0.1, right: worldWidth * 1.9, top: -worldHeight * 0.1, bottom: worldHeight * 1.9 }). If my math is correct, that would allow the world to always have at least 10% of the world visible.

I added options.minScale/maxScale to clampZoom() to hopefully make that plugin easier to use.

davidfig avatar Feb 06 '20 07:02 davidfig

viewport.clampZoom({ minScale: .5, maxScale: 3 }) Indeed, this works for me. Thanks.

In there a way to automatically fit the content?

rodighiero avatar May 19 '20 13:05 rodighiero

viewport.fit() will fit the contents. If you want to use it with clampZoom, use the code from the fit() (or fitWidth/fitHeight) to get the correct amounts.

davidfig avatar May 20 '20 05:05 davidfig

For me, it is difficult to think in terms of width/height fit. It is more natural to think the visualization in terms of scale — perhaps because my navigation is zoom-based. Thanks!

rodighiero avatar May 22 '20 13:05 rodighiero