61 lines
2.6 KiB
JavaScript
61 lines
2.6 KiB
JavaScript
'use client';
|
|
import { useEffect } from 'react';
|
|
import { usePathname } from 'next/navigation';
|
|
const targets = '.reference-service-card, .reference-about-intro, .reference-about-values, .reference-faq, .reference-contact > div, .section-heading, .service-row, .service-detail, .about-band-grid, .contact-cta, .about-intro, .values-grid > div, .faq-section, .contact-info > div, .offices-grid > article, .contact-form, .portfolio-grid > article, .portfolio-detail';
|
|
export default function PageMotion() {
|
|
const pathname = usePathname();
|
|
useEffect(() => {
|
|
const main = document.getElementById('main');
|
|
if (!main || !('IntersectionObserver' in window)) return;
|
|
const preference = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
const seen = new WeakSet();
|
|
const prepared = new Set();
|
|
const observer = new IntersectionObserver(entries => {
|
|
for (const entry of entries) {
|
|
if (!entry.isIntersecting) continue;
|
|
observer.unobserve(entry.target);
|
|
entry.target.dataset.scrollReveal = 'visible';
|
|
}
|
|
}, { threshold: 0, rootMargin: '0px 0px 48px 0px' });
|
|
const prepare = element => {
|
|
if (seen.has(element)) return;
|
|
seen.add(element);
|
|
// Never hide already-visible content, restored scroll positions, or filtered cards.
|
|
if (preference.matches || element.getBoundingClientRect().top < window.innerHeight) return;
|
|
element.dataset.scrollReveal = 'pending';
|
|
prepared.add(element);
|
|
observer.observe(element);
|
|
};
|
|
main.querySelectorAll(targets).forEach(prepare);
|
|
// Inspect added subtrees only; do not rescan the page on every Swiper update.
|
|
const mutations = new MutationObserver(records => {
|
|
for (const record of records) {
|
|
for (const node of record.addedNodes) {
|
|
if (!(node instanceof Element)) continue;
|
|
if (node.matches(targets)) prepare(node);
|
|
node.querySelectorAll(targets).forEach(prepare);
|
|
}
|
|
}
|
|
for (const element of prepared) {
|
|
if (!element.isConnected) {
|
|
observer.unobserve(element);
|
|
prepared.delete(element);
|
|
}
|
|
}
|
|
});
|
|
mutations.observe(main, { childList: true, subtree: true });
|
|
const revealAll = () => {
|
|
observer.disconnect();
|
|
prepared.forEach(element => element.removeAttribute('data-scroll-reveal'));
|
|
prepared.clear();
|
|
};
|
|
const onPreferenceChange = () => { if (preference.matches) revealAll(); };
|
|
preference.addEventListener('change', onPreferenceChange);
|
|
return () => {
|
|
mutations.disconnect();
|
|
revealAll();
|
|
preference.removeEventListener('change', onPreferenceChange);
|
|
};
|
|
}, [pathname]);
|
|
return null;
|
|
} |