Feature: Limit pan distance outside of canvas, e.g. maxPanRatio
See original GitHub issueWhat problem does this feature solve? A user can pan the content outside of the canvas to the point that the content is no longer visible (and even multiple canvas-distances away from the origin), and it may be unintuitive for the user to get the content back inside the visible canvas.
Describe the solution you’d like
An option to provide a max pan distance which would be a proportion of the pannable content, e.g. maxPanRatio: 0.9
representing 90% of the content can be outside of the canvas before panning in that direction is disabled.
Describe alternatives you’ve considered
Unfortunately contain
doesn’t appear to resolve this as we don’t want to limit the element to be inside
the canvas, and outside
didn’t seem to behave as expected for an element initially smaller than the canvas.
I did try contextually enabling and disabling contain: 'outside'
depending on zoom level, but it doesn’t seem like that’s quite supported. That would also be a reasonable solution otherwise.
I have added logic that detects when the event.detail.x
/event.detail.y
values are outside of a specified range and calling pan
to return them to the limit, but it feels a bit hacky and requires a bunch of un-throttled calculations.
Additional context Here’s a snippet of code to give an example of the approach I took. I found it pretty difficult to reason about, so pardon how verbose and yet indecipherable it is. Also, we’re in an untranspiled environment, so its var-city, among other things.
function preventOverPanning(event) {
var detail = event.detail;
var x = detail.x;
var y = detail.y;
var scale = detail.scale;
var mapRect = $map[0].getBoundingClientRect();
var containerRect = $container[0].getBoundingClientRect();
var scaledOffsetX = ((mapRect.width / scale) - mapRect.width) / 2;
var scaledOffsetY = ((mapRect.height / scale) - mapRect.height) / 2;
var relPosX = initRelPosX + scaledOffsetX;
var relPosY = initRelPosY + scaledOffsetY;
var gapX = (containerRect.width - mapRect.width) / 2;
var gapY = (containerRect.height - mapRect.height) / 2;
var maxPanRatio = 0.8; // Percentage of map to allow overflow.
var panX = x * scale + relPosX - gapX;
var maxPanX = mapRect.width * maxPanRatio + gapX;
var isPannedTooFarX = Math.abs(panX) > maxPanX;
var panY = y * scale + relPosY - gapY;
var maxPanY = mapRect.height * maxPanRatio + gapY;
var isPannedTooFarY = Math.abs(panY) > maxPanY;
if (isPannedTooFarX || isPannedTooFarY) {
var newX = x;
var newY = y;
if (isPannedTooFarX) {
var signX = panX / Math.abs(panX);
newX = (maxPanX * signX - relPosX + gapX) / scale;
}
if (isPannedTooFarY) {
var signY = panY / Math.abs(panY);
newY = (maxPanY * signY - relPosY + gapY) / scale;
}
panzoom.pan(newX, newY);
}
}
I do see that there have been issues related to this in the past. Would a PR be welcome, or is this a feature outside the scope of the native library?
Issue Analytics
- State:
- Created 3 years ago
- Reactions:20
- Comments:5 (2 by maintainers)
Top GitHub Comments
I think this have enough votes to implement, and we can use the ratio option instead of hard pixel amount. In other words, only the given amount of the panzoom element will be allowed outside the container.
The
contain: 'inside'
option keeps the element inside the container, andcontain: 'outside'
is meant to be used with elements larger than the container. I didn’t consider a third option where panning would stop when based on a given ratio. You could also do it based on a given pixel amount regardless of scale.Either way, I think this should go through the feature pipeline.
Thanks!