Skip to content

Commit

Permalink
feat(zoom): Add ability to pan around an image on mouse move (#7831)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
broox and nolimits4web authored Jan 2, 2025
1 parent 58593f6 commit c4619bb
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 0 deletions.
69 changes: 69 additions & 0 deletions src/modules/zoom/zoom.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
limitToOriginalSize: false,
maxRatio: 3,
minRatio: 1,
panOnMouseMove: false,
toggle: true,
containerClass: 'swiper-zoom-container',
zoomedSlideClass: 'swiper-slide-zoomed',
Expand All @@ -26,6 +27,9 @@ export default function Zoom({ swiper, extendParams, on, emit }) {

let currentScale = 1;
let isScaling = false;
let isPanningWithMouse = false;
let mousePanStart = { x: 0, y: 0 };
const mousePanSensitivity = -3; // Negative to invert pan direction
let fakeGestureTouched;
let fakeGestureMoved;
let preventZoomOut;
Expand Down Expand Up @@ -262,6 +266,9 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
image.touchesStart.y = event.pageY;
}
function onTouchMove(e) {
const isMouseEvent = e.pointerType === 'mouse';
const isMousePan = isMouseEvent && swiper.params.zoom.panOnMouseMove;

if (!eventWithinSlide(e) || !eventWithinZoomContainer(e)) {
return;
}
Expand All @@ -270,8 +277,14 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
return;
}
if (!image.isTouched || !gesture.slideEl) {
if (isMousePan) onMouseMove(e);
return;
}
if (isMousePan) {
onMouseMove(e);
return;
}

if (!image.isMoved) {
image.width = gesture.imageEl.offsetWidth || gesture.imageEl.clientWidth;
image.height = gesture.imageEl.offsetHeight || gesture.imageEl.clientHeight;
Expand Down Expand Up @@ -435,6 +448,53 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
gesture.originY = 0;
}
}
function onMouseMove(e) {
// Only pan if zoomed in and mouse panning is enabled
if (currentScale <= 1 || !gesture.imageWrapEl) return;
if (!eventWithinSlide(e) || !eventWithinZoomContainer(e)) return;

const currentTransform = window.getComputedStyle(gesture.imageWrapEl).transform;
const matrix = new window.DOMMatrix(currentTransform);

if (!isPanningWithMouse) {
isPanningWithMouse = true;
mousePanStart.x = e.clientX;
mousePanStart.y = e.clientY;

image.startX = matrix.e;
image.startY = matrix.f;
image.width = gesture.imageEl.offsetWidth || gesture.imageEl.clientWidth;
image.height = gesture.imageEl.offsetHeight || gesture.imageEl.clientHeight;

gesture.slideWidth = gesture.slideEl.offsetWidth;
gesture.slideHeight = gesture.slideEl.offsetHeight;
return;
}

const deltaX = (e.clientX - mousePanStart.x) * mousePanSensitivity;
const deltaY = (e.clientY - mousePanStart.y) * mousePanSensitivity;

const scaledWidth = image.width * currentScale;
const scaledHeight = image.height * currentScale;
const slideWidth = gesture.slideWidth;
const slideHeight = gesture.slideHeight;

const minX = Math.min(slideWidth / 2 - scaledWidth / 2, 0);
const maxX = -minX;
const minY = Math.min(slideHeight / 2 - scaledHeight / 2, 0);
const maxY = -minY;

const newX = Math.max(Math.min(image.startX + deltaX, maxX), minX);
const newY = Math.max(Math.min(image.startY + deltaY, maxY), minY);

gesture.imageWrapEl.style.transitionDuration = '0ms';
gesture.imageWrapEl.style.transform = `translate3d(${newX}px, ${newY}px, 0)`;

mousePanStart.x = e.clientX;
mousePanStart.y = e.clientY;
image.startX = newX;
image.startY = newY;
}

function zoomIn(e) {
const zoom = swiper.zoom;
Expand Down Expand Up @@ -598,6 +658,15 @@ export default function Zoom({ swiper, extendParams, on, emit }) {
gesture.slideEl = undefined;
gesture.originX = 0;
gesture.originY = 0;

if (swiper.params.zoom.panOnMouseMove) {
mousePanStart = { x: 0, y: 0 };
if (isPanningWithMouse) {
isPanningWithMouse = false;
image.startX = 0;
image.startY = 0;
}
}
}

// Toggle Zoom
Expand Down
6 changes: 6 additions & 0 deletions src/types/modules/zoom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ export interface ZoomOptions {
* @default 1
*/
minRatio?: number;
/**
* When set to true, a zoomed in image will automatically pan while moving the mouse over the image
*
* @default false
*/
panOnMouseMove?: boolean;
/**
* Enable/disable zoom-in by slide's double tap
*
Expand Down

0 comments on commit c4619bb

Please sign in to comment.