Skip to content

Fix Zoom & pan issues on mobile#4414

Draft
ildyria wants to merge 1 commit into
masterfrom
fix-zoom-pan-issue
Draft

Fix Zoom & pan issues on mobile#4414
ildyria wants to merge 1 commit into
masterfrom
fix-zoom-pan-issue

Conversation

@ildyria

@ildyria ildyria commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

Release Notes

  • New Features
    • Photo gallery now supports pinch-zoom and pan gestures for interactive image exploration.
    • Double-tap to zoom in; double-tap again to reset to original view.
    • Smart gesture handling disables swipe navigation while zoomed to prevent accidental navigation.
    • Enhanced touch interactions optimized for mobile and tablet devices.

@ildyria ildyria requested a review from a team as a code owner June 13, 2026 09:46
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

PhotoBox.vue adds gesture-driven zoom and pan controls for image viewing. Touch events are wired to new gesture handlers, a computed zoom style applies translate and scale transforms to the media, and reactive state tracks pinch-zoom, pan offsets, and double-tap interactions. Zoom resets when switching photos and is bypassed for video/PDF modes.

Changes

Gesture-Based Photo Zoom and Pan

Layer / File(s) Summary
Template event wiring and zoom transforms
resources/js/components/gallery/photoModule/PhotoBox.vue
Root container click and touch events now call handleClick and forward to onTouchStart, onTouchMove, onTouchEnd handlers. Media wrapper applies zoomStyle computed property for translate/scale transforms. Previous inline rotateOverlay click emission replaced by new event flow.
Zoom state and gesture handlers
resources/js/components/gallery/photoModule/PhotoBox.vue
Vue imports expanded to include computed and watch. Zoom/pan reactive state (scale, pan offsets) and gesture tracking variables added. Pinch-zoom and single-finger pan handlers with pan clamping, double-tap zoom/reset, mode-specific Video/Pdf bypass, and watcher to reset state on photo change. Swipe navigation guarded to prevent conflict while zoomed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A pinch and a pan, and a double-tap dance,
Photo views zoom and shift in their prance,
Pan clamped tight, and when photos do change,
Reset the state—no gesture's too strange!
Swipes are now guarded while zoomed in the frame,
Videos untouched, PDF rules the same. 📸

🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
resources/js/components/gallery/photoModule/PhotoBox.vue (1)

143-146: 💤 Low value

Module-level touch tracking variables may retain stale state.

The non-reactive tracking variables (touchStartX, touchStartY, lastPinchDist, lastTapTime) are declared at module scope and will persist across component unmount/remount cycles. While this likely works fine in practice, encapsulating them in a reactive object or resetting them in onUnmounted would provide cleaner lifecycle management.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1b7ca20e-4260-45d8-8665-3dd39d1c1865

📥 Commits

Reviewing files that changed from the base of the PR and between 079839a and d28a341.

📒 Files selected for processing (1)
  • resources/js/components/gallery/photoModule/PhotoBox.vue

Comment on lines +158 to +165
function clampPan(scale: number, x: number, y: number): { x: number; y: number } {
const el = swipe.value;
if (!el) return { x, y };
// Maximum shift before the image edge passes the container edge
const maxX = (el.clientWidth * (scale - 1)) / 2;
const maxY = (el.clientHeight * (scale - 1)) / 2;
return { x: Math.max(-maxX, Math.min(maxX, x)), y: Math.max(-maxY, Math.min(maxY, y)) };
}

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.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Pan clamping calculation assumes image fills the container.

The clampPan function calculates pan boundaries using the container dimensions (el.clientWidth, el.clientHeight), but images may be smaller than the container due to max-w-full and max-h-full constraints. This produces incorrect pan limits when the image doesn't fill the viewport.

The correct calculation should use the actual rendered image dimensions. You can query the image element's offsetWidth and offsetHeight after layout, or compute the effective size from the natural dimensions and container constraints.

💡 Suggested approach
 function clampPan(scale: number, x: number, y: number): { x: number; y: number } {
-  const el = swipe.value;
-  if (!el) return { x, y };
-  // Maximum shift before the image edge passes the container edge
-  const maxX = (el.clientWidth * (scale - 1)) / 2;
-  const maxY = (el.clientHeight * (scale - 1)) / 2;
+  const container = swipe.value;
+  const img = container?.querySelector<HTMLImageElement>("`#image`");
+  if (!container || !img) return { x, y };
+  // Use actual rendered image dimensions
+  const imgW = img.offsetWidth;
+  const imgH = img.offsetHeight;
+  const maxX = Math.max(0, (imgW * scale - container.clientWidth) / 2);
+  const maxY = Math.max(0, (imgH * scale - container.clientHeight) / 2);
   return { x: Math.max(-maxX, Math.min(maxX, x)), y: Math.max(-maxY, Math.min(maxY, y)) };
 }

Note: This approach queries the image element each time. For better performance, consider caching the image dimensions in a ref and updating them when the photo changes or on window resize.

@ildyria ildyria marked this pull request as draft June 13, 2026 10:09
@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.44%. Comparing base (4d3d97b) to head (d28a341).
⚠️ Report is 1 commits behind head on master.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant