SHX-Theme/assets/animations.js

103 lines
3.6 KiB
JavaScript

const SCROLL_ANIMATION_TRIGGER_CLASSNAME = 'scroll-trigger';
const SCROLL_ANIMATION_OFFSCREEN_CLASSNAME = 'scroll-trigger--offscreen';
const SCROLL_ZOOM_IN_TRIGGER_CLASSNAME = 'animate--zoom-in';
const SCROLL_ANIMATION_CANCEL_CLASSNAME = 'scroll-trigger--cancel';
// Scroll in animation logic
function onIntersection(elements, observer) {
elements.forEach((element, index) => {
if (element.isIntersecting) {
const elementTarget = element.target;
if (elementTarget.classList.contains(SCROLL_ANIMATION_OFFSCREEN_CLASSNAME)) {
elementTarget.classList.remove(SCROLL_ANIMATION_OFFSCREEN_CLASSNAME);
if (elementTarget.hasAttribute('data-cascade'))
elementTarget.setAttribute('style', `--animation-order: ${index};`);
}
observer.unobserve(elementTarget);
} else {
element.target.classList.add(SCROLL_ANIMATION_OFFSCREEN_CLASSNAME);
element.target.classList.remove(SCROLL_ANIMATION_CANCEL_CLASSNAME);
}
});
}
function initializeScrollAnimationTrigger(rootEl = document, isDesignModeEvent = false) {
const animationTriggerElements = Array.from(rootEl.getElementsByClassName(SCROLL_ANIMATION_TRIGGER_CLASSNAME));
if (animationTriggerElements.length === 0) return;
if (isDesignModeEvent) {
animationTriggerElements.forEach((element) => {
element.classList.add('scroll-trigger--design-mode');
});
return;
}
const observer = new IntersectionObserver(onIntersection, {
rootMargin: '0px 0px -50px 0px',
});
animationTriggerElements.forEach((element) => observer.observe(element));
}
// Zoom in animation logic
function initializeScrollZoomAnimationTrigger() {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const animationTriggerElements = Array.from(document.getElementsByClassName(SCROLL_ZOOM_IN_TRIGGER_CLASSNAME));
if (animationTriggerElements.length === 0) return;
const scaleAmount = 0.2 / 100;
animationTriggerElements.forEach((element) => {
let elementIsVisible = false;
const observer = new IntersectionObserver((elements) => {
elements.forEach((entry) => {
elementIsVisible = entry.isIntersecting;
});
});
observer.observe(element);
element.style.setProperty('--zoom-in-ratio', 1 + scaleAmount * percentageSeen(element));
window.addEventListener(
'scroll',
throttle(() => {
if (!elementIsVisible) return;
element.style.setProperty('--zoom-in-ratio', 1 + scaleAmount * percentageSeen(element));
}),
{ passive: true }
);
});
}
function percentageSeen(element) {
const viewportHeight = window.innerHeight;
const scrollY = window.scrollY;
const elementPositionY = element.getBoundingClientRect().top + scrollY;
const elementHeight = element.offsetHeight;
if (elementPositionY > scrollY + viewportHeight) {
// If we haven't reached the image yet
return 0;
} else if (elementPositionY + elementHeight < scrollY) {
// If we've completely scrolled past the image
return 100;
}
// When the image is in the viewport
const distance = scrollY + viewportHeight - elementPositionY;
let percentage = distance / ((viewportHeight + elementHeight) / 100);
return Math.round(percentage);
}
window.addEventListener('DOMContentLoaded', () => {
initializeScrollAnimationTrigger();
initializeScrollZoomAnimationTrigger();
});
if (Shopify.designMode) {
document.addEventListener('shopify:section:load', (event) => initializeScrollAnimationTrigger(event.target, true));
document.addEventListener('shopify:section:reorder', () => initializeScrollAnimationTrigger(document, true));
}