Files
site-portfolio/public/js/modules/ui/image-load-placeholder.js
T
root c14bb1f543 fix: доработки слайдера и просмотрщика
- case-media-slider: обрамление+hover как у карточек, стрелки только при наведении (fade-in),
  группа пред/след-пилюля в правом нижнем углу, disabled-кнопки видны (не скрываются),
  убран border-radius у img/figcaption (скругление у viewport), неактивные слайды не скрываются
  visibility (нет 'дырки' при перелистывании), настраиваемая ширина --wide/--content/--natural
- image-load-placeholder: исключён .case-media-slider (нет блика/серого фона)
- icon-press: универсальный эффект 'продавливания' иконки при нажатии (.icon-press + :active/.is-pressed),
  применён к слайдеру и всем кнопкам просмотрщика
2026-09-09 19:29:30 +02:00

75 lines
2.4 KiB
JavaScript

import { isTinyPlaceholderSrc } from "../shared/image-src.js";
const HOST_SELECTOR =
".case-media, .case-media-gallery__item, .case-link-card__media, .case-media-tall__clip, .mp-card__media, .portfolio-card__cover, figure";
const EXCLUDE_ANCESTOR = ".contact-card__icon, .cookie-notice, .case-image-viewer, .case-media-slider, [data-no-img-placeholder]";
function shouldWatch(img) {
if (!(img instanceof HTMLImageElement)) return false;
if (img.closest(EXCLUDE_ANCESTOR) || img.hasAttribute("data-no-img-placeholder")) return false;
const src = (img.getAttribute("src") || "").toLowerCase();
if (src.endsWith(".svg")) return false;
const w = Number(img.getAttribute("width"));
const h = Number(img.getAttribute("height"));
if (w > 0 && h > 0 && Math.max(w, h) <= 40) return false;
return true;
}
function isPending(img) {
if (img.hasAttribute("data-src") || img.hasAttribute("data-defer-src")) return true;
const src = img.currentSrc || img.getAttribute("src") || "";
if (isTinyPlaceholderSrc(src)) return true;
return !img.complete || img.naturalWidth === 0;
}
function getHost(img) {
const host = img.closest(HOST_SELECTOR);
if (host) return host;
const parent = img.parentElement;
if (parent && parent !== document.body && parent !== document.documentElement) return parent;
return null;
}
function setLoading(img, loading) {
const host = getHost(img);
if (!host) return;
if (loading) {
img.classList.add("is-img-loading");
host.classList.add("has-img-loading");
} else {
img.classList.remove("is-img-loading");
if (!host.querySelector("img.is-img-loading")) host.classList.remove("has-img-loading");
}
}
function syncImage(img) {
if (!shouldWatch(img)) {
setLoading(img, false);
return;
}
setLoading(img, isPending(img));
}
function bindImage(img) {
if (!shouldWatch(img)) return;
if (img.dataset.imgLoadPhBound === "1") {
syncImage(img);
return;
}
img.dataset.imgLoadPhBound = "1";
const onChange = () => syncImage(img);
img.addEventListener("load", onChange);
img.addEventListener("error", onChange);
const mo = new MutationObserver(onChange);
mo.observe(img, { attributes: true, attributeFilter: ["src", "data-src", "data-defer-src"] });
syncImage(img);
}
export function initImageLoadPlaceholder() {
document.querySelectorAll("img").forEach(bindImage);
}