Skip to content

Commit

Permalink
Improved animation efficiency
Browse files Browse the repository at this point in the history
  • Loading branch information
prototypa committed Oct 14, 2024
1 parent a5520bb commit 740b96c
Showing 1 changed file with 44 additions and 70 deletions.
114 changes: 44 additions & 70 deletions src/components/common/BasicScripts.astro
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -182,100 +181,75 @@ 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;
if (element.classList.contains('intersect-quarter')) return 0.25;
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;

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));
requestAnimationFrame(() => {
const target = entry.target;
const intersectionRatio = entry.intersectionRatio;
const threshold = target._intersectionThreshold;

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');

const delay = this.animationCounter * this.delayBetweenAnimations;
this.animationCounter++;

target.style.transitionDelay = `${delay}ms`;
target.style.animationDelay = `${delay}ms`;

this.processQueue();
} else {
target.setAttribute('no-intersect', '');
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 = '';

const index = this.visibleElementsQueue.indexOf(target);
if (index > -1) {
this.visibleElementsQueue.splice(index, 1);
this.animationCounter = 0;
}
}
});
});
};

elements.forEach((el) => {
const threshold = getThreshold(el);

if (!this.observers[threshold]) {
this.observers[threshold] = new IntersectionObserver(callback, { threshold });
}
this.observer = new IntersectionObserver(callback.bind(this), { threshold: [0, 0.25, 0.5, 0.99] });

this.observers[threshold].observe(el);
elements.forEach((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();

document.addEventListener('astro:after-swap', () => {
Observer.start();
});
</script>
</script>

0 comments on commit 740b96c

Please sign in to comment.