Improved animation efficiency
This commit is contained in:
@@ -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>
|
Reference in New Issue
Block a user