Skip to content

feat(Viewer): add zoom and pan to image viewer#1756

Open
vladopol wants to merge 7 commits into
nextcloud:mainfrom
vladopol:feat/image-viewer-panzoom
Open

feat(Viewer): add zoom and pan to image viewer#1756
vladopol wants to merge 7 commits into
nextcloud:mainfrom
vladopol:feat/image-viewer-panzoom

Conversation

@vladopol

@vladopol vladopol commented Jun 8, 2026

Copy link
Copy Markdown

Summary

Adds zoom and pan support to the image viewer in Talk Desktop, addressing the long-standing macOS limitation where pinch-to-zoom had no effect and Cmd+/- zoomed the entire application UI instead of the image.

Installs panzoom (v9.4.4, 6 kB) — same library already used in spreed for screen share zoom (spreed#14028).

Changes in ViewerHandlerImages.vue:

  • Scroll wheel / trackpad pinch → zoom in/out (1× – 8×), centered on cursor
  • Drag to pan when zoomed in
  • Double-click to zoom in 3× at cursor position; double-click again to reset to 1×
  • Auto-centers image when zoom returns to 1× (smooth animation)
  • Panning blocked at minimum zoom to prevent accidental image shifts
  • Cursor feedback: zoom-in at 1×, grab / grabbing when zoomed

Test plan

  • Open an image in the viewer
  • Scroll wheel / trackpad pinch zooms image centered on cursor (not top-left corner)
  • Drag image while zoomed in — panning works, cursor shows grab/grabbing
  • Zoom back out to 1× — image smoothly returns to center
  • Double-click zooms in 3× at click point; double-click again resets
  • Open a second image — zoom/pan state resets
  • Verify no regression on video viewer (ViewerHandlerVideos.vue unchanged)

Closes #1535


This PR was developed with AI assistance (Claude Code, claude-sonnet-4-6).

@vladopol vladopol force-pushed the feat/image-viewer-panzoom branch from 6f33707 to e9e75ef Compare June 8, 2026 11:25
@nickvergessen

Copy link
Copy Markdown
Member

You need to sign-off your commits otherwise we can not merge them at all

@vladopol vladopol force-pushed the feat/image-viewer-panzoom branch from e9e75ef to 1fda3da Compare June 8, 2026 11:38
@ShGKme ShGKme added the enhancement New feature or request label Jun 17, 2026
@vladopol

Copy link
Copy Markdown
Author

Fixed the JSDoc lint errors (jsdoc/require-param-type) in the latest commit. Could you approve the CI run?

@ShGKme ShGKme left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution. All good in general, but there are some problems.


We have a new AI Policy. If this was created with AI, could you mention it in the PR description and add the Assisted-by: git trailer?

Comment thread src/talk/renderer/Viewer/ViewerHandlerImages.vue
Comment thread src/talk/renderer/Viewer/ViewerHandlerImages.vue
Comment thread src/talk/renderer/Viewer/ViewerHandlerImages.vue Outdated
Comment thread src/talk/renderer/Viewer/ViewerHandlerImages.vue Outdated
@load="handleLoadEnd(false)"
@error="handleLoadEnd(true)">
<ViewerHandlerMedia v-slot="{ handleLoadEnd }">
<div class="viewer-image-container" @dblclick.capture="onDoubleClick">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Why does it need the capture mode (and preventDefault + stopPropagation in the handler)?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .capture modifier is needed because panzoom registers its own dblclick listener on the same element — without capture, both handlers fire and the image zooms twice (once by panzoom's built-in handler, once by ours). Intercepting in the capture phase lets us handle it first and call stopPropagation to prevent panzoom's handler from running.

preventDefault suppresses the browser's default double-click text selection behavior. stopPropagation also prevents the event from bubbling up to the parent Viewer, which closes on double-click.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Indeed, panzoom doesn't allow customizing the double-click behavior directly via the panzoom options...

One nitpick: preventDefault and stopPropagation can also be set via Vue like capture mode is, to not mix Vue and JS ways for the same event:

@dblclick.capture.stop.prevent="onDoubleClick"

Comment thread src/talk/renderer/Viewer/ViewerHandlerImages.vue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue(blocking): currently it is possible to move the image even on scale: 1, moving it almost completely away:

Initial Moved
Image Image

It should only be movable on the axis, if the image on this axis eexceeds its container limits.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is fixed now for scale: 1, but zoomed it is possible to move the image completely out of the viewport.

vladopol added 4 commits June 17, 2026 20:33
Install panzoom library (9.4.4, 6 kB) and apply it to the image
viewer wrapper in ViewerHandlerImages.vue:

- Scroll wheel to zoom in/out (1× – 8×)
- Drag to pan when zoomed in
- Double-click to zoom in 3× at cursor, double-click again to reset
- Auto-center image when zoom returns to 1×
- Pan blocked at minimum zoom to prevent accidental shifts
- Cursor changes: zoom-in at 1×, grab/grabbing when zoomed

Fixes nextcloud#1535

Signed-off-by: Vladimir Poluliashenko <vladopol@gmail.com>
Assisted-by: ClaudeCode:claude-sonnet-4-6
Signed-off-by: Vladimir Poluliashenko <vladopol@gmail.com>
Assisted-by: ClaudeCode:claude-sonnet-4-6
…lement

Signed-off-by: Vladimir Poluliashenko <vladopol@gmail.com>
Assisted-by: ClaudeCode:claude-sonnet-4-6
- Use useTemplateRef API (Vue 3.5+) for panzoom wrapper ref
- Use plain variable for panzoom instance (no reactive state needed)
- Remove unnecessary null guard in initPanzoom (wrapperRef is always
  defined when called from onImageLoad)
- Add comment explaining dblclick.capture usage

Signed-off-by: Vladimir Poluliashenko <vladopol@gmail.com>
Assisted-by: ClaudeCode:claude-sonnet-4-6
@vladopol vladopol force-pushed the feat/image-viewer-panzoom branch from 4ec6e63 to 6d3ea60 Compare June 17, 2026 19:34
Replace panzoom's built-in bounds option (ineffective when the panzoom
element fills its parent 100%) with manual clamping via
getBoundingClientRect. clampToBounds() is called on panend and after
each zoom to smoothly return the image within the viewport if needed.

Signed-off-by: Vladimir Poluliashenko <vladopol@gmail.com>
Assisted-by: ClaudeCode:claude-sonnet-4-6
@ShGKme

ShGKme commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

There is still the same problem for me:

Recording.2026-06-18.165650.mp4

@ShGKme ShGKme added the AI assisted This PR contains AI-assisted commits label Jun 18, 2026
- Clamp on every pan event (not just panend) for immediate stopping
- Disable smoothScroll (inertia) so the image halts on release without
  overshooting the boundary

Signed-off-by: Vladimir Poluliashenko <vladopol@gmail.com>
Assisted-by: ClaudeCode:claude-sonnet-4-6
@ShGKme

ShGKme commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Pan just gets stuck when the original image (not the container) is reached.

Recording.2026-06-18.173040.mp4

@ShGKme

ShGKme commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Also, zooming out with double-click may result in the image being completely disappeared depending on the double-click position...

smoothZoomAbs(x, y, 0) scaled the image to zero, making it disappear.
Using ZOOM_MIN (1) correctly returns the image to its original size.

Signed-off-by: Vladyslav Polulyashenko <vladopol@gmail.com>
Assisted-by: ClaudeCode:claude-sonnet-4-6
@github-actions

Copy link
Copy Markdown
Contributor

Hello there,
Thank you so much for taking the time and effort to create a pull request to our Nextcloud project.

We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process.

Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6

Thank you for contributing to Nextcloud and we hope to hear from you soon!

(If you believe you should not receive this message, you can add yourself to the blocklist.)

@ShGKme

ShGKme commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

After zooming out on the second double click, the image is stuck in some uneven position, and neither pan nor zoom works again.

problem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI assisted This PR contains AI-assisted commits enhancement New feature or request feedback-requested

Projects

None yet

Development

Successfully merging this pull request may close these issues.

macOS: Zoom on pictures

3 participants