panzoom icon indicating copy to clipboard operation
panzoom copied to clipboard

Feature: New contain option for both inside/outside

Open jcnventura opened this issue 2 years ago • 9 comments

What problem does this feature solve? At the moment, when choosing the contain:inside option, it is not possible to zoom the element outside the parent element. Conversely, the contain:outside option doesn't allow to zoom the element to become smaller than the parent element in both dimensions.

Describe the solution you'd like The new option would allow the element to be contained, with the current functionality of the contain:outside when either of the dimensions of the element is larger than the parent element, and with the functionality of contain:inside otherwise. This would provide a way for the element to never be able to be zoomed or panned outside of the parent element.

This new option could probably be called true or contained. I'd steer away from both, as that would be invalidated if any future contain option is added (see #476).

Describe alternatives you've considered None, really. At the moment the inside/outside options are too limiting and mutually exclusive, and not setting a contain option allows the user to pan/zoom the element outside of the parent element, which is not really desirable.

Additional context The patch to the code would look like this (note this was a patch vs the dist/panzoom.js file and not the src/panzoom.js):

@@ -484,6 +484,7 @@
         }
         function constrainXY(toX, toY, toScale, panOptions) {
             var opts = __assign(__assign({}, options), panOptions);
+            var contain = opts.contain;
             var result = { x: x, y: y, opts: opts };
             if (!opts.force && (opts.disablePan || (opts.panOnlyWhenZoomed && scale === opts.startScale))) {
                 return result;
@@ -496,7 +497,7 @@
             if (!opts.disableYAxis) {
                 result.y = (opts.relative ? y : 0) + toY;
             }
-            if (opts.contain) {
+            if (contain) {
                 var dims = getDimensions(elem);
                 var realWidth = dims.elem.width / scale;
                 var realHeight = dims.elem.height / scale;
@@ -504,7 +505,10 @@
                 var scaledHeight = realHeight * toScale;
                 var diffHorizontal = (scaledWidth - realWidth) / 2;
                 var diffVertical = (scaledHeight - realHeight) / 2;
-                if (opts.contain === 'inside') {
+                if (contain === 'true') {
+                    contain = ((scaledWidth > dims.parent.width) || (scaledHeight > dims.parent.height)) ? 'outside' : 'inside';
+                }
+                if (contain === 'inside') {
                     var minX = (-dims.elem.margin.left - dims.parent.padding.left + diffHorizontal) / toScale;
                     var maxX = (dims.parent.width -
                         scaledWidth -
@@ -526,7 +530,7 @@
                         toScale;
                     result.y = Math.max(Math.min(result.y, maxY), minY);
                 }
-                else if (opts.contain === 'outside') {
+                else if (contain === 'outside') {
                     var minX = (-(scaledWidth - dims.parent.width) -
                         dims.parent.padding.left -
                         dims.parent.border.left -

jcnventura avatar Dec 19 '21 19:12 jcnventura

Hi,

Are there any updates planned for this feature? We are currently using this library and I believe the problem this issue describes is the only thing stopping us from fully adopting it. Essentially there doesn't seem to be a way to stop the user from dragging the image out of view while maintaining the ability to pan and zoom at all times. As described (in possibly another issue here) contain: "outside" comes close but the inability to pan or zoom out in the initial state is a non-starter.

Fingel avatar Jan 25 '22 00:01 Fingel

Sorry I haven't been able to get to it. I'll see if I can make some time this week.

timmywil avatar Jan 25 '22 15:01 timmywil

Thank you! Appreciate it. Let us know if it's something we can help with.

Fingel avatar Jan 25 '22 19:01 Fingel

@timmywil Is it possible to keep #504 in mind when looking at this? This ticket sounds similar but not sure if the author means exactly the same.

Edit: Also #577

mythjuha avatar Jan 30 '22 07:01 mythjuha

does it work, if does, how? please tell me.

shasabbir avatar Jun 23 '22 10:06 shasabbir

@timmywil any chance of seeing this feature included in a new version in the near future?

I would really like to have this without having to resort to using one of the forks which have included it (but also look like they haven't been updated further with any of the latest changes to this original library).

I am facing an issue similar to this, where the container to my panzoom instance can be vertically resized and have it's height become higher than the image / element to be panned. In this situation, , with contain: outside, the image will stay in place while the container is resized but once the user drags to pan again, the image will just 'jump' to align at the bottom of panzoom container.

Currently I am stuck between either not setting the contain option and allowing the image to be panned/zoomed outside of the container, or using contain: outside with the weird jump alignment after resizing the container, neither of which are really desirable behaviors for my use case.

JoseAlmeidaCamenAi avatar Nov 17 '22 14:11 JoseAlmeidaCamenAi

Loving this library, except that lack of this feature is making it hard to use for our purposes.

I'm not sure if everyone is after the same thing, but I think it might help to think of this as a problem that should be solved for each axis separately. As has been pointed out before, sometimes you want 'inside' on one axis and 'outside' on the other.

I'd argue that contain should be a boolean value, and if true these rules should be applied on each axis:

  • If the content length exceeds the container length
    • we can pan on this axis, but the edges of the content cannot enter the container
  • If the content length is equal to the container length:
    • we cannot pan on this axis
  • If the content length is less than the container length, either: (config setting)
    • panOnlyWhenOverlap: true: The content is centered on this axis and cannot be panned on this axis
    • panOnlyWhenOverlap: false: The content can be panned on this axis, but the edges of the content cannot leave the container

Those rules wouldn't place any limits on zooming, but could cooperate with some zoom settings:

  • minScale: 'fit': the content can be shrunk until it is equal length to the container on one axis, and shorter than (or equal to) the container on the other axis
  • minScale: 'fill': the content can be shrunk until it is equal length to the container on one axis, and larger than (or equal to) the container on the other axis
  • maxScale: 'fit': the content can be zoomed until it is equal length to the container on one axis, and shorter than (or equal to) the container on the other axis
  • maxScale: 'fill': the content can be zoomed until it is equal length to the container on one axis, and larger than (or equal to) the container on the other axis

I think these rules and config settings combined could potentially replicate the existing containment modes, which could be retained for backwards compatibility:

  • contain: 'outside' is equivalent to minScale: 'fill'
  • contain: 'inside' is equivalent to maxScale: 'fit'

Not sure if I'm thinking straight anymore 🤪 so apologies if this doesn't make sense. Anyway, here is a codepen and video below that illustrates my use case (panOnlyWhenOverlap: true).

https://user-images.githubusercontent.com/1079425/218231896-d1348a6b-0e5d-43c5-b16f-7db46f49c0c4.mov

p.s. alternately scale keywords could match css conventions: fit -> contain, fill => cover

jonom avatar Feb 11 '23 01:02 jonom

This feature would fit my use-case of a mobile phone pan/zoom image nicely. At the moment, the viewport is smaller than the image. I'd like to make the min zoom level the same as the viewport width (and/or height) so it can't be scrolled off-screen.

JohnHardy avatar Mar 15 '23 01:03 JohnHardy

Are there any updates for when this issue could be resolved?

eric-g-97477 avatar Mar 25 '24 17:03 eric-g-97477