Files
Personal-Website/client/js/reveal.js
T

59 lines
1.9 KiB
JavaScript

export function initReveal() {
const items = document.querySelectorAll('[data-reveal]');
if (!('IntersectionObserver' in window)) {
for (const item of items) item.classList.add('is-visible');
return;
}
const groups = new Map();
const observer = new IntersectionObserver(
(entries) => {
// things that come into view together get a small stagger
let index = 0;
for (const entry of entries) {
if (!entry.isIntersecting) continue;
for (const item of groups.get(entry.target)) {
item.style.setProperty('--delay', `${Math.min(index++ * 90, 450)}ms`);
item.classList.add('is-visible');
}
observer.unobserve(entry.target);
}
},
{ rootMargin: '0px 0px -6% 0px' },
);
for (const item of items) {
// Chrome applies the element's own clip-path, so a fully clipped heading never
// counts as intersecting. Watch the parent for those instead.
const target = item.dataset.reveal === 'mask' ? item.parentElement : item;
if (!groups.has(target)) groups.set(target, []);
groups.get(target).push(item);
observer.observe(target);
}
}
// Fallback for browsers without scroll-driven animations
export function initTimeline() {
const timeline = document.querySelector('[data-timeline]');
if (!timeline || CSS.supports('animation-timeline: view()')) return;
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
let ticking = false;
const update = () => {
const rect = timeline.getBoundingClientRect();
const progress = (innerHeight * 0.75 - rect.top) / rect.height;
timeline.style.setProperty('--progress', Math.min(1, Math.max(0, progress)).toFixed(3));
ticking = false;
};
addEventListener(
'scroll',
() => {
if (!ticking) requestAnimationFrame(update);
ticking = true;
},
{ passive: true },
);
update();
}