Add map image lightbox overlay with zoom animation on click

This commit is contained in:
hbrain 2026-05-31 14:55:56 +02:00
parent 5a81f48be0
commit a52e0a8bb3
4 changed files with 147 additions and 12 deletions

View file

@ -291,5 +291,6 @@ if ($titleExact && ($filters['f_title'] ?? '') !== '') $baseParams['f_exact'] =
}); });
})(); })();
</script> </script>
<script src="js/map_lightbox.js" defer></script>
</body> </body>
</html> </html>

75
js/map_lightbox.js Normal file
View file

@ -0,0 +1,75 @@
(() => {
const SELECTOR = 'img.map-image';
let lightbox = null;
let lightboxImg = null;
function ensureLightbox() {
if (lightbox) return lightbox;
lightbox = document.createElement('div');
lightbox.className = 'map-lightbox';
lightbox.setAttribute('aria-hidden', 'true');
const closeBtn = document.createElement('button');
closeBtn.type = 'button';
closeBtn.className = 'map-lightbox__close';
closeBtn.setAttribute('aria-label', 'Close map preview');
closeBtn.textContent = '×';
lightboxImg = document.createElement('img');
lightboxImg.className = 'map-lightbox__img';
lightboxImg.alt = 'Map preview';
const hint = document.createElement('div');
hint.className = 'map-lightbox__hint';
hint.textContent = 'Esc or click outside to close';
lightbox.appendChild(closeBtn);
lightbox.appendChild(lightboxImg);
lightbox.appendChild(hint);
document.body.appendChild(lightbox);
closeBtn.addEventListener('click', closeLightbox);
lightbox.addEventListener('click', (ev) => {
if (ev.target === lightbox) closeLightbox();
});
document.addEventListener('keydown', (ev) => {
if (ev.key === 'Escape' && lightbox.classList.contains('is-open')) {
closeLightbox();
}
});
return lightbox;
}
function openLightbox(src, alt) {
ensureLightbox();
lightboxImg.src = src;
lightboxImg.alt = alt || 'Map preview';
lightbox.classList.add('is-open');
lightbox.setAttribute('aria-hidden', 'false');
document.body.classList.add('map-lightbox-open');
}
function closeLightbox() {
if (!lightbox) return;
lightbox.classList.remove('is-open');
lightbox.setAttribute('aria-hidden', 'true');
document.body.classList.remove('map-lightbox-open');
setTimeout(() => {
if (!lightbox.classList.contains('is-open')) {
lightboxImg.removeAttribute('src');
}
}, 260);
}
document.addEventListener('click', (ev) => {
const target = ev.target instanceof Element ? ev.target.closest(SELECTOR) : null;
if (!target) return;
ev.preventDefault();
ev.stopPropagation();
openLightbox(target.currentSrc || target.src, target.alt || 'Map preview');
});
})();

View file

@ -1098,8 +1098,5 @@ function applySwitchPayload(payload){
})(); })();
</script> </script>
<script src="js/map_lightbox.js" defer></script>
</body></html> </body></html>

View file

@ -45,14 +45,6 @@
} }
.describe-button{display:inline-block;margin-right:.6rem} .describe-button{display:inline-block;margin-right:.6rem}
.map-image{
width: 100%;
aspect-ratio: 16/9;
background: #111315;
border-radius: .5rem;
margin-top: 1rem;
}
.map-image { .map-image {
width: 100%; width: 100%;
aspect-ratio: 16 / 9; aspect-ratio: 16 / 9;
@ -60,8 +52,78 @@
background: #111315; background: #111315;
border-radius: .5rem; border-radius: .5rem;
margin-top: 1rem; margin-top: 1rem;
cursor: zoom-in;
transition: transform .22s ease, box-shadow .22s ease;
}
.map-image:hover {
transform: translateY(-1px);
box-shadow: 0 8px 22px rgba(0,0,0,.35);
} }
.video-wrapper { .video-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
body.map-lightbox-open { overflow: hidden; }
.map-lightbox {
position: fixed;
inset: 0;
z-index: 99999;
display: grid;
place-items: center;
background: rgba(11, 13, 15, .86);
backdrop-filter: blur(4px);
opacity: 0;
pointer-events: none;
transition: opacity .22s ease;
padding: 2.2rem;
box-sizing: border-box;
}
.map-lightbox.is-open {
opacity: 1;
pointer-events: auto;
}
.map-lightbox__img {
max-width: min(96vw, 2300px);
max-height: 92vh;
width: auto;
height: auto;
object-fit: contain;
border-radius: .65rem;
box-shadow: 0 20px 70px rgba(0,0,0,.65);
transform: scale(.9);
opacity: 0;
transition: transform .24s cubic-bezier(.2,.75,.15,1), opacity .24s ease;
}
.map-lightbox.is-open .map-lightbox__img {
transform: scale(1);
opacity: 1;
}
.map-lightbox__close {
position: fixed;
top: 1.1rem;
right: 1.1rem;
width: 2.2rem;
height: 2.2rem;
border-radius: 50%;
border: 1px solid #C46A3A;
background: #C46A3A;
color: #111315;
font-size: 1.15rem;
line-height: 1;
font-weight: 700;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: 0 3px 12px rgba(0,0,0,.45);
}
.map-lightbox__close:hover { background: #d97b48; border-color: #d97b48; }
.map-lightbox__hint {
position: fixed;
bottom: .9rem;
left: 50%;
transform: translateX(-50%);
color: #A0A4AB;
font-size: .82rem;
}