Skip to content

Commit 740b96c

Browse files
committed
Improved animation efficiency
1 parent a5520bb commit 740b96c

File tree

1 file changed

+44
-70
lines changed

1 file changed

+44
-70
lines changed

src/components/common/BasicScripts.astro

+44-70
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,9 @@ import { UI } from 'astrowind:config';
164164
<script is:inline>
165165
/* Inspired by: https://github.com/heidkaemper/tailwindcss-intersect */
166166
const Observer = {
167-
observers: {},
168-
visibleElementsQueue: [],
169-
processing: false,
167+
observer: null,
170168
delayBetweenAnimations: 100,
169+
animationCounter: 0,
171170

172171
start() {
173172
const selectors = [
@@ -182,100 +181,75 @@ import { UI } from 'astrowind:config';
182181

183182
const elements = Array.from(document.querySelectorAll(selectors.join(',')));
184183

185-
elements.forEach((el) => el.setAttribute('no-intersect', ''));
186-
187184
const getThreshold = (element) => {
188185
if (element.classList.contains('intersect-full')) return 0.99;
189186
if (element.classList.contains('intersect-half')) return 0.5;
190187
if (element.classList.contains('intersect-quarter')) return 0.25;
191188
return 0;
192189
};
193190

194-
Object.values(this.observers).forEach((observer) => observer.disconnect());
195-
this.observers = {};
191+
elements.forEach((el) => {
192+
el.setAttribute('no-intersect', '');
193+
el._intersectionThreshold = getThreshold(el);
194+
});
196195

197196
const callback = (entries) => {
198197
entries.forEach((entry) => {
199-
const target = entry.target;
200-
201-
if (entry.isIntersecting) {
202-
if (target.classList.contains('intercept-no-queue')) {
203-
target.removeAttribute('no-intersect');
204-
if (target.classList.contains('intersect-once')) {
205-
Object.values(this.observers).forEach((observer) => observer.unobserve(target));
198+
requestAnimationFrame(() => {
199+
const target = entry.target;
200+
const intersectionRatio = entry.intersectionRatio;
201+
const threshold = target._intersectionThreshold;
202+
203+
if (target.classList.contains('intersect-no-queue')) {
204+
if (entry.isIntersecting) {
205+
target.removeAttribute('no-intersect');
206+
if (target.classList.contains('intersect-once')) {
207+
this.observer.unobserve(target);
208+
}
209+
} else {
210+
target.setAttribute('no-intersect', '');
206211
}
207212
return;
208213
}
209214

210-
if (!this.visibleElementsQueue.includes(target)) {
211-
this.visibleElementsQueue.push(target);
212-
}
215+
if (intersectionRatio >= threshold) {
216+
if (!target.hasAttribute('data-animated')) {
217+
target.removeAttribute('no-intersect');
218+
target.setAttribute('data-animated', 'true');
219+
220+
const delay = this.animationCounter * this.delayBetweenAnimations;
221+
this.animationCounter++;
222+
223+
target.style.transitionDelay = `${delay}ms`;
224+
target.style.animationDelay = `${delay}ms`;
213225

214-
this.processQueue();
215-
} else {
216-
target.setAttribute('no-intersect', '');
226+
if (target.classList.contains('intersect-once')) {
227+
this.observer.unobserve(target);
228+
}
229+
}
230+
} else {
231+
target.setAttribute('no-intersect', '');
232+
target.removeAttribute('data-animated');
233+
target.style.transitionDelay = '';
234+
target.style.animationDelay = '';
217235

218-
const index = this.visibleElementsQueue.indexOf(target);
219-
if (index > -1) {
220-
this.visibleElementsQueue.splice(index, 1);
236+
this.animationCounter = 0;
221237
}
222-
}
238+
});
223239
});
224240
};
225241

226-
elements.forEach((el) => {
227-
const threshold = getThreshold(el);
228-
229-
if (!this.observers[threshold]) {
230-
this.observers[threshold] = new IntersectionObserver(callback, { threshold });
231-
}
242+
this.observer = new IntersectionObserver(callback.bind(this), { threshold: [0, 0.25, 0.5, 0.99] });
232243

233-
this.observers[threshold].observe(el);
244+
elements.forEach((el) => {
245+
this.observer.observe(el);
234246
});
235247
},
236-
237-
async processQueue() {
238-
if (this.processing) {
239-
return;
240-
}
241-
242-
this.processing = true;
243-
244-
while (this.visibleElementsQueue.length > 0) {
245-
const element = this.visibleElementsQueue.shift();
246-
247-
element.removeAttribute('no-intersect');
248-
249-
if (element.classList.contains('intersect-once')) {
250-
Object.values(this.observers).forEach((observer) => observer.unobserve(element));
251-
}
252-
253-
if (this.isElementInViewport(element)) {
254-
await this.delay(this.delayBetweenAnimations);
255-
}
256-
}
257-
258-
this.processing = false;
259-
},
260-
261-
delay(ms) {
262-
return new Promise((resolve) => setTimeout(resolve, ms));
263-
},
264-
265-
isElementInViewport(element) {
266-
const rect = element.getBoundingClientRect();
267-
return (
268-
rect.top < (window.innerHeight || document.documentElement.clientHeight) &&
269-
rect.bottom > 0 &&
270-
rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
271-
rect.right > 0
272-
);
273-
},
274248
};
275249

276250
Observer.start();
277251

278252
document.addEventListener('astro:after-swap', () => {
279253
Observer.start();
280254
});
281-
</script>
255+
</script>

0 commit comments

Comments
 (0)