Skip to content

Commit c4619bb

Browse files
brooxnolimits4web
andauthored
feat(zoom): Add ability to pan around an image on mouse move (#7831)
* Add ability to pan around an image by mouse to close #7306 * Make the zoom module's panWithMouse param more descriptive * reuse onTouchMove event --------- Co-authored-by: Vladimir Kharlampidi <[email protected]>
1 parent 58593f6 commit c4619bb

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

src/modules/zoom/zoom.mjs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
1414
limitToOriginalSize: false,
1515
maxRatio: 3,
1616
minRatio: 1,
17+
panOnMouseMove: false,
1718
toggle: true,
1819
containerClass: 'swiper-zoom-container',
1920
zoomedSlideClass: 'swiper-slide-zoomed',
@@ -26,6 +27,9 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
2627

2728
let currentScale = 1;
2829
let isScaling = false;
30+
let isPanningWithMouse = false;
31+
let mousePanStart = { x: 0, y: 0 };
32+
const mousePanSensitivity = -3; // Negative to invert pan direction
2933
let fakeGestureTouched;
3034
let fakeGestureMoved;
3135
let preventZoomOut;
@@ -262,6 +266,9 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
262266
image.touchesStart.y = event.pageY;
263267
}
264268
function onTouchMove(e) {
269+
const isMouseEvent = e.pointerType === 'mouse';
270+
const isMousePan = isMouseEvent && swiper.params.zoom.panOnMouseMove;
271+
265272
if (!eventWithinSlide(e) || !eventWithinZoomContainer(e)) {
266273
return;
267274
}
@@ -270,8 +277,14 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
270277
return;
271278
}
272279
if (!image.isTouched || !gesture.slideEl) {
280+
if (isMousePan) onMouseMove(e);
281+
return;
282+
}
283+
if (isMousePan) {
284+
onMouseMove(e);
273285
return;
274286
}
287+
275288
if (!image.isMoved) {
276289
image.width = gesture.imageEl.offsetWidth || gesture.imageEl.clientWidth;
277290
image.height = gesture.imageEl.offsetHeight || gesture.imageEl.clientHeight;
@@ -435,6 +448,53 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
435448
gesture.originY = 0;
436449
}
437450
}
451+
function onMouseMove(e) {
452+
// Only pan if zoomed in and mouse panning is enabled
453+
if (currentScale <= 1 || !gesture.imageWrapEl) return;
454+
if (!eventWithinSlide(e) || !eventWithinZoomContainer(e)) return;
455+
456+
const currentTransform = window.getComputedStyle(gesture.imageWrapEl).transform;
457+
const matrix = new window.DOMMatrix(currentTransform);
458+
459+
if (!isPanningWithMouse) {
460+
isPanningWithMouse = true;
461+
mousePanStart.x = e.clientX;
462+
mousePanStart.y = e.clientY;
463+
464+
image.startX = matrix.e;
465+
image.startY = matrix.f;
466+
image.width = gesture.imageEl.offsetWidth || gesture.imageEl.clientWidth;
467+
image.height = gesture.imageEl.offsetHeight || gesture.imageEl.clientHeight;
468+
469+
gesture.slideWidth = gesture.slideEl.offsetWidth;
470+
gesture.slideHeight = gesture.slideEl.offsetHeight;
471+
return;
472+
}
473+
474+
const deltaX = (e.clientX - mousePanStart.x) * mousePanSensitivity;
475+
const deltaY = (e.clientY - mousePanStart.y) * mousePanSensitivity;
476+
477+
const scaledWidth = image.width * currentScale;
478+
const scaledHeight = image.height * currentScale;
479+
const slideWidth = gesture.slideWidth;
480+
const slideHeight = gesture.slideHeight;
481+
482+
const minX = Math.min(slideWidth / 2 - scaledWidth / 2, 0);
483+
const maxX = -minX;
484+
const minY = Math.min(slideHeight / 2 - scaledHeight / 2, 0);
485+
const maxY = -minY;
486+
487+
const newX = Math.max(Math.min(image.startX + deltaX, maxX), minX);
488+
const newY = Math.max(Math.min(image.startY + deltaY, maxY), minY);
489+
490+
gesture.imageWrapEl.style.transitionDuration = '0ms';
491+
gesture.imageWrapEl.style.transform = `translate3d(${newX}px, ${newY}px, 0)`;
492+
493+
mousePanStart.x = e.clientX;
494+
mousePanStart.y = e.clientY;
495+
image.startX = newX;
496+
image.startY = newY;
497+
}
438498

439499
function zoomIn(e) {
440500
const zoom = swiper.zoom;
@@ -598,6 +658,15 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
598658
gesture.slideEl = undefined;
599659
gesture.originX = 0;
600660
gesture.originY = 0;
661+
662+
if (swiper.params.zoom.panOnMouseMove) {
663+
mousePanStart = { x: 0, y: 0 };
664+
if (isPanningWithMouse) {
665+
isPanningWithMouse = false;
666+
image.startX = 0;
667+
image.startY = 0;
668+
}
669+
}
601670
}
602671

603672
// Toggle Zoom

src/types/modules/zoom.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ export interface ZoomOptions {
6363
* @default 1
6464
*/
6565
minRatio?: number;
66+
/**
67+
* When set to true, a zoomed in image will automatically pan while moving the mouse over the image
68+
*
69+
* @default false
70+
*/
71+
panOnMouseMove?: boolean;
6672
/**
6773
* Enable/disable zoom-in by slide's double tap
6874
*

0 commit comments

Comments
 (0)