Improved animation efficiency

This commit is contained in:
prototypa
2024-10-14 10:19:11 -04:00
parent a5520bbc85
commit 740b96c878

View File

@@ -164,10 +164,9 @@ import { UI } from 'astrowind:config';
<script is:inline>
/* Inspired by: https://github.com/heidkaemper/tailwindcss-intersect */
const Observer = {
observers: {},
visibleElementsQueue: [],
processing: false,
observer: null,
delayBetweenAnimations: 100,
animationCounter: 0,
start() {
const selectors = [
@@ -182,8 +181,6 @@ import { UI } from 'astrowind:config';
const elements = Array.from(document.querySelectorAll(selectors.join(',')));
elements.forEach((el) => el.setAttribute('no-intersect', ''));
const getThreshold = (element) => {
if (element.classList.contains('intersect-full')) return 0.99;
if (element.classList.contains('intersect-half')) return 0.5;
@@ -191,86 +188,63 @@ import { UI } from 'astrowind:config';
return 0;
};
Object.values(this.observers).forEach((observer) => observer.disconnect());
this.observers = {};
elements.forEach((el) => {
el.setAttribute('no-intersect', '');
el._intersectionThreshold = getThreshold(el);
});
const callback = (entries) => {
entries.forEach((entry) => {
const target = entry.target;
requestAnimationFrame(() => {
const target = entry.target;
const intersectionRatio = entry.intersectionRatio;
const threshold = target._intersectionThreshold;
if (entry.isIntersecting) {
if (target.classList.contains('intercept-no-queue')) {
target.removeAttribute('no-intersect');
if (target.classList.contains('intersect-once')) {
Object.values(this.observers).forEach((observer) => observer.unobserve(target));
if (target.classList.contains('intersect-no-queue')) {
if (entry.isIntersecting) {
target.removeAttribute('no-intersect');
if (target.classList.contains('intersect-once')) {
this.observer.unobserve(target);
}
} else {
target.setAttribute('no-intersect', '');
}
return;
}
if (!this.visibleElementsQueue.includes(target)) {
this.visibleElementsQueue.push(target);
}
if (intersectionRatio >= threshold) {
if (!target.hasAttribute('data-animated')) {
target.removeAttribute('no-intersect');
target.setAttribute('data-animated', 'true');
this.processQueue();
} else {
target.setAttribute('no-intersect', '');
const delay = this.animationCounter * this.delayBetweenAnimations;
this.animationCounter++;
const index = this.visibleElementsQueue.indexOf(target);
if (index > -1) {
this.visibleElementsQueue.splice(index, 1);
target.style.transitionDelay = `${delay}ms`;
target.style.animationDelay = `${delay}ms`;
if (target.classList.contains('intersect-once')) {
this.observer.unobserve(target);
}
}
} else {
target.setAttribute('no-intersect', '');
target.removeAttribute('data-animated');
target.style.transitionDelay = '';
target.style.animationDelay = '';
this.animationCounter = 0;
}
}
});
});
};
this.observer = new IntersectionObserver(callback.bind(this), { threshold: [0, 0.25, 0.5, 0.99] });
elements.forEach((el) => {
const threshold = getThreshold(el);
if (!this.observers[threshold]) {
this.observers[threshold] = new IntersectionObserver(callback, { threshold });
}
this.observers[threshold].observe(el);
this.observer.observe(el);
});
},
async processQueue() {
if (this.processing) {
return;
}
this.processing = true;
while (this.visibleElementsQueue.length > 0) {
const element = this.visibleElementsQueue.shift();
element.removeAttribute('no-intersect');
if (element.classList.contains('intersect-once')) {
Object.values(this.observers).forEach((observer) => observer.unobserve(element));
}
if (this.isElementInViewport(element)) {
await this.delay(this.delayBetweenAnimations);
}
}
this.processing = false;
},
delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
},
isElementInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top < (window.innerHeight || document.documentElement.clientHeight) &&
rect.bottom > 0 &&
rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
rect.right > 0
);
},
};
Observer.start();
@@ -278,4 +252,4 @@ import { UI } from 'astrowind:config';
document.addEventListener('astro:after-swap', () => {
Observer.start();
});
</script>
</script>