@@ -164,10 +164,9 @@ import { UI } from 'astrowind:config';
164
164
<script is:inline >
165
165
/* Inspired by: https://github.com/heidkaemper/tailwindcss-intersect */
166
166
const Observer = {
167
- observers: {},
168
- visibleElementsQueue: [],
169
- processing: false,
167
+ observer: null,
170
168
delayBetweenAnimations: 100,
169
+ animationCounter: 0,
171
170
172
171
start() {
173
172
const selectors = [
@@ -182,100 +181,75 @@ import { UI } from 'astrowind:config';
182
181
183
182
const elements = Array.from(document.querySelectorAll(selectors.join(',')));
184
183
185
- elements.forEach((el) => el.setAttribute('no-intersect', ''));
186
-
187
184
const getThreshold = (element) => {
188
185
if (element.classList.contains('intersect-full')) return 0.99;
189
186
if (element.classList.contains('intersect-half')) return 0.5;
190
187
if (element.classList.contains('intersect-quarter')) return 0.25;
191
188
return 0;
192
189
};
193
190
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
+ });
196
195
197
196
const callback = (entries) => {
198
197
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', '');
206
211
}
207
212
return;
208
213
}
209
214
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`;
213
225
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 = '';
217
235
218
- const index = this.visibleElementsQueue.indexOf(target);
219
- if (index > -1) {
220
- this.visibleElementsQueue.splice(index, 1);
236
+ this.animationCounter = 0;
221
237
}
222
- }
238
+ });
223
239
});
224
240
};
225
241
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] });
232
243
233
- this.observers[threshold].observe(el);
244
+ elements.forEach((el) => {
245
+ this.observer.observe(el);
234
246
});
235
247
},
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
- },
274
248
};
275
249
276
250
Observer.start();
277
251
278
252
document.addEventListener('astro:after-swap', () => {
279
253
Observer.start();
280
254
});
281
- </script >
255
+ </script >
0 commit comments