216 lines
6.4 KiB
JavaScript
216 lines
6.4 KiB
JavaScript
const qs = (selector, scope = document) => scope.querySelector(selector);
|
|
const qsa = (selector, scope = document) => [...scope.querySelectorAll(selector)];
|
|
|
|
const initHeader = () => {
|
|
const header = qs(".site-header");
|
|
const toggle = qs("[data-menu-toggle]");
|
|
const panel = qs("[data-mobile-panel]");
|
|
const page = document.body.dataset.page;
|
|
let scrollTicking = false;
|
|
|
|
if (page) {
|
|
qsa(`[data-nav="${page}"]`).forEach((link) => link.classList.add("is-active"));
|
|
}
|
|
|
|
const updateHeader = () => {
|
|
if (!header) return;
|
|
header.classList.toggle("is-scrolled", window.scrollY > 24);
|
|
scrollTicking = false;
|
|
};
|
|
|
|
const onScroll = () => {
|
|
if (scrollTicking) return;
|
|
scrollTicking = true;
|
|
window.requestAnimationFrame(updateHeader);
|
|
};
|
|
|
|
updateHeader();
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
|
|
if (toggle && panel) {
|
|
toggle.addEventListener("click", () => {
|
|
const isOpen = panel.classList.toggle("is-open");
|
|
document.body.classList.toggle("menu-open", isOpen);
|
|
toggle.setAttribute("aria-expanded", String(isOpen));
|
|
const icon = qs("[data-menu-icon]", toggle);
|
|
if (icon) icon.setAttribute("data-lucide", isOpen ? "x" : "menu");
|
|
if (window.lucide) window.lucide.createIcons();
|
|
});
|
|
|
|
qsa("a", panel).forEach((link) => {
|
|
link.addEventListener("click", () => {
|
|
panel.classList.remove("is-open");
|
|
document.body.classList.remove("menu-open");
|
|
toggle.setAttribute("aria-expanded", "false");
|
|
const icon = qs("[data-menu-icon]", toggle);
|
|
if (icon) icon.setAttribute("data-lucide", "menu");
|
|
if (window.lucide) window.lucide.createIcons();
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
const initSmoothAnchors = () => {
|
|
const smoothMotion = window.matchMedia("(prefers-reduced-motion: no-preference)").matches && window.matchMedia("(min-width: 768px)").matches;
|
|
|
|
qsa('a[href^="#"]').forEach((anchor) => {
|
|
anchor.addEventListener("click", (event) => {
|
|
const target = qs(anchor.getAttribute("href"));
|
|
if (!target) return;
|
|
event.preventDefault();
|
|
target.scrollIntoView({ behavior: smoothMotion ? "smooth" : "auto", block: "start" });
|
|
});
|
|
});
|
|
};
|
|
|
|
const initAnimations = () => {
|
|
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
|
|
if (window.AOS) {
|
|
AOS.init({
|
|
once: true,
|
|
duration: 560,
|
|
easing: "ease-out-cubic",
|
|
offset: 70,
|
|
disable: () => window.innerWidth < 640 || reducedMotion
|
|
});
|
|
}
|
|
};
|
|
|
|
const initHeroReveal = () => {
|
|
qsa(".reveal-text").forEach((item, index) => {
|
|
item.style.animationDelay = `${Math.min(index * 70, 420)}ms`;
|
|
});
|
|
};
|
|
|
|
const initGallery = () => {
|
|
const buttons = qsa("[data-filter]");
|
|
const cards = qsa("[data-category]");
|
|
if (!buttons.length || !cards.length) return;
|
|
|
|
buttons.forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
const filter = button.dataset.filter;
|
|
buttons.forEach((item) => item.classList.remove("is-active"));
|
|
button.classList.add("is-active");
|
|
|
|
cards.forEach((card) => {
|
|
const show = filter === "all" || card.dataset.category === filter;
|
|
card.style.display = show ? "inline-block" : "none";
|
|
});
|
|
|
|
if (window.AOS) AOS.refreshHard();
|
|
});
|
|
});
|
|
};
|
|
|
|
const initImageHints = () => {
|
|
const fallback = "assets/images/gallery/studio-fallback.svg";
|
|
|
|
const applyFallback = (img) => {
|
|
if (img.dataset.fallbackApplied === "true") return;
|
|
img.dataset.fallbackApplied = "true";
|
|
img.src = fallback;
|
|
img.alt = img.alt || "Venus Photo Studio And Lab image preview";
|
|
|
|
const lightboxTrigger = img.closest("[data-lightbox]");
|
|
if (lightboxTrigger) {
|
|
lightboxTrigger.dataset.lightbox = fallback;
|
|
}
|
|
};
|
|
|
|
qsa("img").forEach((img) => {
|
|
img.decoding = "async";
|
|
|
|
if (!img.hasAttribute("loading") && !img.closest(".site-header")) {
|
|
img.loading = "lazy";
|
|
}
|
|
|
|
img.addEventListener("error", () => applyFallback(img), { once: true });
|
|
|
|
if (img.complete && img.naturalWidth === 0) {
|
|
applyFallback(img);
|
|
}
|
|
});
|
|
};
|
|
|
|
const initLightbox = () => {
|
|
const triggers = qsa("[data-lightbox]");
|
|
if (!triggers.length) return;
|
|
|
|
const lightbox = document.createElement("div");
|
|
lightbox.className = "lightbox";
|
|
lightbox.setAttribute("role", "dialog");
|
|
lightbox.setAttribute("aria-modal", "true");
|
|
lightbox.innerHTML = `
|
|
<button class="lightbox-close" type="button" aria-label="Close preview">
|
|
<i data-lucide="x"></i>
|
|
</button>
|
|
<img alt="">
|
|
`;
|
|
document.body.appendChild(lightbox);
|
|
|
|
const image = qs("img", lightbox);
|
|
const close = qs(".lightbox-close", lightbox);
|
|
|
|
const closeLightbox = () => {
|
|
lightbox.classList.remove("is-open");
|
|
document.body.classList.remove("menu-open");
|
|
image.removeAttribute("src");
|
|
image.alt = "";
|
|
};
|
|
|
|
triggers.forEach((trigger) => {
|
|
trigger.addEventListener("click", () => {
|
|
const source = trigger.dataset.lightbox || qs("img", trigger)?.src;
|
|
const caption = trigger.dataset.caption || qs(".gallery-caption", trigger)?.textContent?.trim() || "Venus Photo Studio And Lab gallery preview";
|
|
if (!source) return;
|
|
image.src = source;
|
|
image.alt = caption;
|
|
lightbox.classList.add("is-open");
|
|
document.body.classList.add("menu-open");
|
|
if (window.lucide) window.lucide.createIcons();
|
|
});
|
|
});
|
|
|
|
close.addEventListener("click", closeLightbox);
|
|
lightbox.addEventListener("click", (event) => {
|
|
if (event.target === lightbox) closeLightbox();
|
|
});
|
|
window.addEventListener("keydown", (event) => {
|
|
if (event.key === "Escape") closeLightbox();
|
|
});
|
|
};
|
|
|
|
const initContactForm = () => {
|
|
const form = qs("[data-contact-form]");
|
|
const note = qs("[data-form-note]");
|
|
if (!form || !note) return;
|
|
|
|
form.addEventListener("submit", (event) => {
|
|
event.preventDefault();
|
|
note.textContent = "Thank you. Your enquiry is ready to send through the connected contact channel.";
|
|
note.classList.remove("hidden");
|
|
form.reset();
|
|
});
|
|
};
|
|
|
|
const initFooterYear = () => {
|
|
qsa("[data-year]").forEach((item) => {
|
|
item.textContent = String(new Date().getFullYear());
|
|
});
|
|
};
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
initHeader();
|
|
initSmoothAnchors();
|
|
initHeroReveal();
|
|
initAnimations();
|
|
initGallery();
|
|
initLightbox();
|
|
initContactForm();
|
|
initImageHints();
|
|
initFooterYear();
|
|
if (window.lucide) window.lucide.createIcons();
|
|
});
|