Skip to content

Feature: Limit pan distance outside of canvas, e.g. maxPanRatio #476

Open
@michaeljonathanblack

Description

@michaeljonathanblack

What 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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions