Skip to content

Content "jumps" slightly when hitting MinZoom or MaxZoom #131

@greenwaym

Description

@greenwaym

When zooming reaches the minimum or maximum zoom limit, there's a slight unwanted pan shift.
The content "jumps" slightly when hitting the zoom boundary, then shifts back when zooming
in the opposite direction.

Steps to reproduce:

  1. Set MinZoomX="0.5", MaxZoomX="2.0" (or any limits)
  2. Position content so a specific point is under the cursor
  3. Zoom in repeatedly until hitting the max limit
  4. Expected: Content stays stationary under cursor when limit is reached
  5. Actual: Content shifts slightly when the limit is hit

Cause

The issue might be in the interaction between ZoomTo() and Constrain().

ZoomTo() applies the full zoom ratio via ScaleAtPrepend, which calculates both scale and
translation to keep the zoom center point stationary, even if we exceed the zoom limit here. A code snippet from line 2231 to 2248:

// Use effective zoom limits that consider auto-calculated bounds
GetEffectiveZoomLimits(out var effectiveMinZoomX, out var effectiveMaxZoomX, out var effectiveMinZoomY, out var effectiveMaxZoomY);

if ((ZoomX >= effectiveMaxZoomX && ZoomY >= effectiveMaxZoomY && ratio > 1) || 
    (ZoomX <= effectiveMinZoomX && ZoomY <= effectiveMinZoomY && ratio < 1))
{
    return;
}

_updating = true;

Log("[ZoomTo]");
var previousMatrix = _matrix;
var previousZoomX = _zoomX;
var previousZoomY = _zoomY;

_matrix = MatrixHelper.ScaleAtPrepend(_matrix, ratio, ratio, x, y);
Invalidate(skipTransitions);

So we see here in the ZoomTo method, we return early if we already exceed the max or min zoom. We don't check whether our current zoom delta exceeds the max or min zoom though. The Invalidate call at the end brings us to line 1965, where we call the Constrain function in line 1977.

The Constrain method (line 1810) now clamps zoomX and zoomY to the Zoom limits. OffsetX and OffsetY aren't recalculated for the new zoom value though (and actually can't be here, as we don't have any information about the center point x and y here as we do in ZoomTo)

Potential solution

I think we might have to calculate effective ratio (clamped) in ZoomTo before calling ScaleAtPrepend, so that the ScaleAtPrepend creates the matrix with the correct offset values for the clamped ratio. We already call GetEffectiveZoomLimits in ZoomTo, so calculating the clamped ratio here should be possible without larger changes to the codebase. I think this also applies to the Zoom function.

By the way, do you accept pull requests? I will most likely work with PanAndZoom for a while, as it really helps me in a current project of mine. So when I encounter minor issues, I could fix them myself if that would help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions