From 415d95ed47d19818ae29fc583391f4a42df4c52b Mon Sep 17 00:00:00 2001 From: Mischa Date: Sun, 21 Jun 2026 21:29:06 +0200 Subject: [PATCH] feat(photoswipe): animate keyboard arrow navigation in lightbox PhotoSwipe's goTo() moves slides instantly (no spring animation unlike swipe). Intercepts keydown in capture phase, sets a CSS transition and forces a reflow before PhotoSwipe moves the container, so the browser animates from the old position to the new one. Cleans up on close. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr --- themes/intotheeast/templates/dailies.html.twig | 11 +++++++++++ themes/intotheeast/templates/home.html.twig | 11 +++++++++++ themes/intotheeast/templates/trip.html.twig | 11 +++++++++++ 3 files changed, 33 insertions(+) diff --git a/themes/intotheeast/templates/dailies.html.twig b/themes/intotheeast/templates/dailies.html.twig index 3f4d007..a963138 100644 --- a/themes/intotheeast/templates/dailies.html.twig +++ b/themes/intotheeast/templates/dailies.html.twig @@ -110,6 +110,17 @@ const lightbox = new PhotoSwipeLightbox({ children: 'a.journal-photo-slide', pswpModule: () => import('https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.esm.min.js') }); +lightbox.on('afterOpen', function () { + var container = lightbox.pswp.element.querySelector('.pswp__container'); + function onKey(e) { + if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return; + container.style.transition = 'transform 0.35s cubic-bezier(0.4, 0, 0.22, 1)'; + container.getBoundingClientRect(); + setTimeout(function () { container.style.transition = ''; }, 400); + } + document.addEventListener('keydown', onKey, true); + lightbox.pswp.on('close', function () { document.removeEventListener('keydown', onKey, true); }); +}); lightbox.init(); /* Per-strip: dot sync + expand button → tap the visible slide to trigger pswp */ diff --git a/themes/intotheeast/templates/home.html.twig b/themes/intotheeast/templates/home.html.twig index 9614745..33ca3fd 100644 --- a/themes/intotheeast/templates/home.html.twig +++ b/themes/intotheeast/templates/home.html.twig @@ -145,6 +145,17 @@ const lightbox = new PhotoSwipeLightbox({ children: 'a.journal-photo-slide', pswpModule: () => import('https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.esm.min.js') }); +lightbox.on('afterOpen', function () { + var container = lightbox.pswp.element.querySelector('.pswp__container'); + function onKey(e) { + if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return; + container.style.transition = 'transform 0.35s cubic-bezier(0.4, 0, 0.22, 1)'; + container.getBoundingClientRect(); + setTimeout(function () { container.style.transition = ''; }, 400); + } + document.addEventListener('keydown', onKey, true); + lightbox.pswp.on('close', function () { document.removeEventListener('keydown', onKey, true); }); +}); lightbox.init(); document.querySelectorAll('.journal-photo-wrap').forEach(function (wrap) { diff --git a/themes/intotheeast/templates/trip.html.twig b/themes/intotheeast/templates/trip.html.twig index 4f38710..97a6ee7 100644 --- a/themes/intotheeast/templates/trip.html.twig +++ b/themes/intotheeast/templates/trip.html.twig @@ -514,6 +514,17 @@ const lightbox = new PhotoSwipeLightbox({ children: 'a.journal-photo-slide', pswpModule: () => import('https://cdn.jsdelivr.net/npm/photoswipe@5/dist/photoswipe.esm.min.js') }); +lightbox.on('afterOpen', function () { + var container = lightbox.pswp.element.querySelector('.pswp__container'); + function onKey(e) { + if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return; + container.style.transition = 'transform 0.35s cubic-bezier(0.4, 0, 0.22, 1)'; + container.getBoundingClientRect(); // force reflow so browser commits current position as "from" + setTimeout(function () { container.style.transition = ''; }, 400); + } + document.addEventListener('keydown', onKey, true); + lightbox.pswp.on('close', function () { document.removeEventListener('keydown', onKey, true); }); +}); lightbox.init(); document.querySelectorAll('.journal-photo-wrap').forEach(function (wrap) {