Improve card image hover: center small, pan large
This commit is contained in:
@@ -543,6 +543,7 @@ function buildCard(s, { showAuthor = false, allowDelete = false, allowEdit = fal
|
|||||||
`;
|
`;
|
||||||
if (hasImage) {
|
if (hasImage) {
|
||||||
const btn = card.querySelector(".card-visual");
|
const btn = card.querySelector(".card-visual");
|
||||||
|
setupCardVisualHover(btn, s.screenshotUrl);
|
||||||
btn.addEventListener("click", () => openLightbox(s.screenshotUrl, s.name));
|
btn.addEventListener("click", () => openLightbox(s.screenshotUrl, s.name));
|
||||||
}
|
}
|
||||||
if (allowEdit) {
|
if (allowEdit) {
|
||||||
@@ -669,6 +670,50 @@ function openLightbox(url, title) {
|
|||||||
document.body.appendChild(overlay);
|
document.body.appendChild(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupCardVisualHover(el, url) {
|
||||||
|
if (!el || !url) return;
|
||||||
|
const img = new Image();
|
||||||
|
let naturalW = 0;
|
||||||
|
let naturalH = 0;
|
||||||
|
let loaded = false;
|
||||||
|
img.src = url;
|
||||||
|
img.onload = () => {
|
||||||
|
naturalW = img.naturalWidth;
|
||||||
|
naturalH = img.naturalHeight;
|
||||||
|
loaded = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
el.classList.remove("hovering");
|
||||||
|
el.style.backgroundSize = "";
|
||||||
|
el.style.backgroundPosition = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
el.addEventListener("mouseenter", () => {
|
||||||
|
el.classList.add("hovering");
|
||||||
|
el.style.backgroundSize = "auto";
|
||||||
|
el.style.backgroundPosition = "center";
|
||||||
|
});
|
||||||
|
|
||||||
|
el.addEventListener("mousemove", (e) => {
|
||||||
|
if (!loaded) return;
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
const overW = naturalW - rect.width;
|
||||||
|
const overH = naturalH - rect.height;
|
||||||
|
if (overW <= 0 && overH <= 0) {
|
||||||
|
el.style.backgroundPosition = "center";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const xRatio = (e.clientX - rect.left) / rect.width;
|
||||||
|
const yRatio = (e.clientY - rect.top) / rect.height;
|
||||||
|
const xPercent = overW > 0 ? xRatio * 100 : 50;
|
||||||
|
const yPercent = overH > 0 ? yRatio * 100 : 50;
|
||||||
|
el.style.backgroundPosition = `${xPercent}% ${yPercent}%`;
|
||||||
|
});
|
||||||
|
|
||||||
|
["mouseleave", "blur"].forEach(evt => el.addEventListener(evt, reset));
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
setupHandlers();
|
setupHandlers();
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -161,12 +161,15 @@ button.ghost { background: transparent; border-color: #d5c7b5; color: #2c1c0d; }
|
|||||||
background: linear-gradient(135deg, #f0d9b5, #f6b24f);
|
background: linear-gradient(135deg, #f0d9b5, #f6b24f);
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-color: #f6b24f;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: none;
|
border: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
.card-visual.hovering { cursor: zoom-in; }
|
||||||
|
|
||||||
.card-body { padding: 12px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
|
.card-body { padding: 12px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user