Files
TaroTime/app/ui-tarot-lightbox.js
2026-03-07 05:17:50 -08:00

176 lines
4.5 KiB
JavaScript

(function () {
"use strict";
let overlayEl = null;
let imageEl = null;
let zoomed = false;
const LIGHTBOX_ZOOM_SCALE = 6.66;
function resetZoom() {
if (!imageEl) {
return;
}
zoomed = false;
imageEl.style.transform = "scale(1)";
imageEl.style.transformOrigin = "center center";
imageEl.style.cursor = "zoom-in";
}
function updateZoomOrigin(clientX, clientY) {
if (!zoomed || !imageEl) {
return;
}
const rect = imageEl.getBoundingClientRect();
if (!rect.width || !rect.height) {
return;
}
const x = Math.min(100, Math.max(0, ((clientX - rect.left) / rect.width) * 100));
const y = Math.min(100, Math.max(0, ((clientY - rect.top) / rect.height) * 100));
imageEl.style.transformOrigin = `${x}% ${y}%`;
}
function isPointOnCard(clientX, clientY) {
if (!imageEl) {
return false;
}
const rect = imageEl.getBoundingClientRect();
const naturalWidth = imageEl.naturalWidth;
const naturalHeight = imageEl.naturalHeight;
if (!rect.width || !rect.height || !naturalWidth || !naturalHeight) {
return true;
}
const frameAspect = rect.width / rect.height;
const imageAspect = naturalWidth / naturalHeight;
let renderWidth = rect.width;
let renderHeight = rect.height;
if (imageAspect > frameAspect) {
renderHeight = rect.width / imageAspect;
} else {
renderWidth = rect.height * imageAspect;
}
const left = rect.left + (rect.width - renderWidth) / 2;
const top = rect.top + (rect.height - renderHeight) / 2;
const right = left + renderWidth;
const bottom = top + renderHeight;
return clientX >= left && clientX <= right && clientY >= top && clientY <= bottom;
}
function ensure() {
if (overlayEl && imageEl) {
return;
}
overlayEl = document.createElement("div");
overlayEl.setAttribute("aria-hidden", "true");
overlayEl.style.position = "fixed";
overlayEl.style.inset = "0";
overlayEl.style.background = "rgba(0, 0, 0, 0.82)";
overlayEl.style.display = "none";
overlayEl.style.alignItems = "center";
overlayEl.style.justifyContent = "center";
overlayEl.style.zIndex = "9999";
overlayEl.style.padding = "0";
imageEl = document.createElement("img");
imageEl.alt = "Tarot card enlarged image";
imageEl.style.maxWidth = "100vw";
imageEl.style.maxHeight = "100vh";
imageEl.style.width = "100vw";
imageEl.style.height = "100vh";
imageEl.style.objectFit = "contain";
imageEl.style.borderRadius = "0";
imageEl.style.boxShadow = "none";
imageEl.style.border = "none";
imageEl.style.cursor = "zoom-in";
imageEl.style.transform = "scale(1)";
imageEl.style.transformOrigin = "center center";
imageEl.style.transition = "transform 120ms ease-out";
imageEl.style.userSelect = "none";
overlayEl.appendChild(imageEl);
const close = () => {
if (!overlayEl || !imageEl) {
return;
}
overlayEl.style.display = "none";
overlayEl.setAttribute("aria-hidden", "true");
imageEl.removeAttribute("src");
resetZoom();
};
overlayEl.addEventListener("click", (event) => {
if (event.target === overlayEl) {
close();
}
});
imageEl.addEventListener("click", (event) => {
event.stopPropagation();
if (!isPointOnCard(event.clientX, event.clientY)) {
close();
return;
}
if (!zoomed) {
zoomed = true;
imageEl.style.transform = `scale(${LIGHTBOX_ZOOM_SCALE})`;
imageEl.style.cursor = "zoom-out";
updateZoomOrigin(event.clientX, event.clientY);
return;
}
resetZoom();
});
imageEl.addEventListener("mousemove", (event) => {
updateZoomOrigin(event.clientX, event.clientY);
});
imageEl.addEventListener("mouseleave", () => {
if (zoomed) {
imageEl.style.transformOrigin = "center center";
}
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
close();
}
});
document.body.appendChild(overlayEl);
}
function open(src, altText) {
if (!src) {
return;
}
ensure();
if (!overlayEl || !imageEl) {
return;
}
imageEl.src = src;
imageEl.alt = altText || "Tarot card enlarged image";
resetZoom();
overlayEl.style.display = "flex";
overlayEl.setAttribute("aria-hidden", "false");
}
window.TarotUiLightbox = {
...(window.TarotUiLightbox || {}),
open
};
})();