Add swipe and keyboard navigation to the pure image zoom overlay

Arrow keys and touch swipe now navigate between plates while the
zoom is open, keeping the lightbox in sync underneath. goToZoomSlide
clamps to list bounds and delegates to goToSlide for thumb/index sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 14:52:05 +02:00
parent 3b359d2b37
commit be24904b78
+22 -2
View File
@@ -212,6 +212,7 @@
let lbBuilt = false; let lbBuilt = false;
let lbReferrer = null; // URL to return to on close let lbReferrer = null; // URL to return to on close
let imgZoomOpen = false; let imgZoomOpen = false;
let imgZoomIdx = -1;
function lbOpen(slug, scopedList) { function lbOpen(slug, scopedList) {
lbList = scopedList || POSTS; lbList = scopedList || POSTS;
@@ -350,8 +351,8 @@
document.addEventListener('keydown', e => { document.addEventListener('keydown', e => {
if (lb.dataset.open !== 'true') return; if (lb.dataset.open !== 'true') return;
if (e.key === 'Escape') { if (imgZoomOpen) closeImgZoom(); else lbClose(); } if (e.key === 'Escape') { if (imgZoomOpen) closeImgZoom(); else lbClose(); }
if (e.key === 'ArrowLeft') { e.preventDefault(); goToSlide(lbIdx - 1); } if (e.key === 'ArrowLeft') { e.preventDefault(); imgZoomOpen ? goToZoomSlide(imgZoomIdx - 1) : goToSlide(lbIdx - 1); }
if (e.key === 'ArrowRight') { e.preventDefault(); goToSlide(lbIdx + 1); } if (e.key === 'ArrowRight') { e.preventDefault(); imgZoomOpen ? goToZoomSlide(imgZoomIdx + 1) : goToSlide(lbIdx + 1); }
}); });
// Touch swipe // Touch swipe
@@ -477,6 +478,7 @@
function openImgZoom(src) { function openImgZoom(src) {
if (!imgZoom || !imgZoomImg || !src) return; if (!imgZoom || !imgZoomImg || !src) return;
imgZoomImg.src = src; imgZoomImg.src = src;
imgZoomIdx = lbIdx;
imgZoom.dataset.open = 'true'; imgZoom.dataset.open = 'true';
imgZoomOpen = true; imgZoomOpen = true;
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
@@ -489,6 +491,14 @@
if (!lb || lb.dataset.open !== 'true') document.body.style.overflow = ''; if (!lb || lb.dataset.open !== 'true') document.body.style.overflow = '';
} }
function goToZoomSlide(idx) {
if (!lbList.length || !imgZoomImg) return;
imgZoomIdx = Math.max(0, Math.min(idx, lbList.length - 1));
const p = lbList[imgZoomIdx];
if (p) imgZoomImg.src = p.card || p.thumb || '';
goToSlide(imgZoomIdx);
}
// Hero "View full image" button // Hero "View full image" button
const viewFullBtn = document.getElementById('viewFull'); const viewFullBtn = document.getElementById('viewFull');
if (viewFullBtn) { if (viewFullBtn) {
@@ -505,6 +515,16 @@
if (imgZoom) { if (imgZoom) {
imgZoom.addEventListener('click', e => { if (e.target === imgZoom) closeImgZoom(); }); imgZoom.addEventListener('click', e => { if (e.target === imgZoom) closeImgZoom(); });
document.getElementById('imgZoomClose')?.addEventListener('click', closeImgZoom); document.getElementById('imgZoomClose')?.addEventListener('click', closeImgZoom);
// Swipe to navigate
let zoomTouchX = null;
imgZoom.addEventListener('touchstart', e => { zoomTouchX = e.touches[0].clientX; }, { passive: true });
imgZoom.addEventListener('touchend', e => {
if (zoomTouchX === null) return;
const dx = e.changedTouches[0].clientX - zoomTouchX;
if (Math.abs(dx) > 50) dx < 0 ? goToZoomSlide(imgZoomIdx + 1) : goToZoomSlide(imgZoomIdx - 1);
zoomTouchX = null;
});
} }
// Escape when lightbox is NOT open (standalone zoom) // Escape when lightbox is NOT open (standalone zoom)