fix: zoom

This commit is contained in:
Tim Rijkse
2026-01-16 10:09:44 +01:00
parent 939a4e9c57
commit 69e4f160fe

View File

@@ -14,12 +14,14 @@ class ImageGallery extends HTMLElement {
this.isDragging = false;
this.startX = 0;
this.startY = 0;
this.lastTranslateX = 0;
this.lastTranslateY = 0;
// Pinch zoom state
this.initialPinchDistance = 0;
this.initialZoom = 1;
this.pinchCenterX = 0;
this.pinchCenterY = 0;
this.initialTranslateX = 0;
this.initialTranslateY = 0;
}
connectedCallback() {
@@ -117,16 +119,56 @@ class ImageGallery extends HTMLElement {
e.preventDefault();
e.stopPropagation();
const delta = e.deltaY > 0 ? -0.1 : 0.1;
this.zoom(delta);
const delta = e.deltaY > 0 ? -0.15 : 0.15;
// Get mouse position relative to modal content center
const modalContent = this.shadowRoot.querySelector(".modal-content");
const rect = modalContent.getBoundingClientRect();
const mouseX = e.clientX - rect.left - rect.width / 2;
const mouseY = e.clientY - rect.top - rect.height / 2;
this.zoomToPoint(delta, mouseX, mouseY);
}
zoomToPoint(delta, pointX, pointY) {
const modalImage = this.shadowRoot.querySelector(".modal-image");
if (!modalImage) return;
const prevZoom = this.currentZoom;
const newZoom = Math.max(1, Math.min(4, this.currentZoom + delta));
if (newZoom === prevZoom) return;
// Calculate new translate to zoom towards point
if (newZoom === 1) {
this.translateX = 0;
this.translateY = 0;
} else {
const zoomRatio = newZoom / prevZoom;
this.translateX = pointX - (pointX - this.translateX) * zoomRatio;
this.translateY = pointY - (pointY - this.translateY) * zoomRatio;
}
this.currentZoom = newZoom;
this.updateImageTransform(false);
modalImage.style.cursor = this.currentZoom > 1 ? "grab" : "default";
}
handleTouchStart(e) {
if (e.touches.length === 2) {
// Pinch zoom start
e.preventDefault();
this.isDragging = false; // Stop any ongoing drag
this.initialPinchDistance = this.getPinchDistance(e.touches);
this.initialZoom = this.currentZoom;
this.initialTranslateX = this.translateX;
this.initialTranslateY = this.translateY;
// Get pinch center relative to viewport
const modalContent = this.shadowRoot.querySelector(".modal-content");
const rect = modalContent.getBoundingClientRect();
this.pinchCenterX = ((e.touches[0].clientX + e.touches[1].clientX) / 2) - rect.left - rect.width / 2;
this.pinchCenterY = ((e.touches[0].clientY + e.touches[1].clientY) / 2) - rect.top - rect.height / 2;
} else if (e.touches.length === 1 && this.currentZoom > 1) {
// Single touch drag when zoomed
this.startDrag(e);
@@ -134,21 +176,27 @@ class ImageGallery extends HTMLElement {
}
handleTouchMove(e) {
if (e.touches.length === 2) {
// Pinch zoom
if (e.touches.length === 2 && this.initialPinchDistance > 0) {
// Pinch zoom with zoom-to-point
e.preventDefault();
const currentDistance = this.getPinchDistance(e.touches);
const scale = currentDistance / this.initialPinchDistance;
const newZoom = Math.max(1, Math.min(4, this.initialZoom * scale));
const prevZoom = this.currentZoom;
this.currentZoom = newZoom;
if (this.currentZoom === 1) {
this.translateX = 0;
this.translateY = 0;
} else {
// Adjust translate to zoom towards pinch center
const zoomRatio = this.currentZoom / this.initialZoom;
this.translateX = this.pinchCenterX - (this.pinchCenterX - this.initialTranslateX) * zoomRatio;
this.translateY = this.pinchCenterY - (this.pinchCenterY - this.initialTranslateY) * zoomRatio;
}
this.updateImageTransform();
this.updateImageTransform(false); // No transition during pinch
const modalImage = this.shadowRoot.querySelector(".modal-image");
if (modalImage) {
@@ -209,7 +257,12 @@ class ImageGallery extends HTMLElement {
this.translateX = clientX - this.startX;
this.translateY = clientY - this.startY;
this.updateImageTransform();
// Use no transition during drag for smooth movement
const modalImage = this.shadowRoot.querySelector(".modal-image");
if (modalImage) {
modalImage.style.transition = "none";
modalImage.style.transform = `scale(${this.currentZoom}) translate(${this.translateX / this.currentZoom}px, ${this.translateY / this.currentZoom}px)`;
}
}
endDrag() {
@@ -224,10 +277,19 @@ class ImageGallery extends HTMLElement {
}
}
updateImageTransform() {
updateImageTransform(useTransition = true) {
const modalImage = this.shadowRoot.querySelector(".modal-image");
if (modalImage) {
if (!useTransition) {
modalImage.style.transition = "none";
}
modalImage.style.transform = `scale(${this.currentZoom}) translate(${this.translateX / this.currentZoom}px, ${this.translateY / this.currentZoom}px)`;
// Force reflow and restore transition
if (!useTransition) {
modalImage.offsetHeight; // Force reflow
modalImage.style.transition = "transform 0.2s ease";
}
}
}
@@ -238,13 +300,25 @@ class ImageGallery extends HTMLElement {
if (modal && modalImage && this.images[index]) {
modalImage.src = this.images[index];
// Reset zoom and pan
// Reset all state
this.currentZoom = 1;
this.translateX = 0;
this.translateY = 0;
this.updateImageTransform();
this.isDragging = false;
this.initialPinchDistance = 0;
this.initialZoom = 1;
this.initialTranslateX = 0;
this.initialTranslateY = 0;
// Reset transform without transition
modalImage.style.transition = "none";
modalImage.style.transform = "scale(1) translate(0px, 0px)";
modalImage.style.cursor = "default";
// Force reflow then restore transition
modalImage.offsetHeight;
modalImage.style.transition = "transform 0.2s ease";
modal.classList.add("open");
document.body.style.overflow = "hidden";
}
@@ -278,10 +352,19 @@ class ImageGallery extends HTMLElement {
resetZoom() {
const modalImage = this.shadowRoot.querySelector(".modal-image");
if (modalImage) {
// Reset all state
this.currentZoom = 1;
this.translateX = 0;
this.translateY = 0;
this.updateImageTransform();
this.isDragging = false;
this.initialPinchDistance = 0;
this.initialZoom = 1;
this.initialTranslateX = 0;
this.initialTranslateY = 0;
// Ensure transition is enabled for smooth reset animation
modalImage.style.transition = "transform 0.3s ease";
modalImage.style.transform = "scale(1) translate(0px, 0px)";
modalImage.style.cursor = "default";
}
}