From 03bf12bdeb3b7f28c169d96af8134ea7282cb741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAmjad?= Date: Thu, 4 Jun 2026 04:51:41 +0500 Subject: [PATCH 1/2] Improve animation performance Batch animation reads and writes so the browser lays out once instead of once per element (removes layout thrash in the reveal path). Co-Authored-By: Claude Opus 4.8 --- assets/js/animations-interactivity.js | 85 +++++++++++++++------------ 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/assets/js/animations-interactivity.js b/assets/js/animations-interactivity.js index 4bd0c38c..184294ec 100644 --- a/assets/js/animations-interactivity.js +++ b/assets/js/animations-interactivity.js @@ -58,12 +58,16 @@ }); trackedElements = []; - + + // Read every element's geometry before any class/style write. Interleaving + // getBoundingClientRect with writes forces a synchronous layout per element + // (layout thrash) - expensive on pages with many animated blocks. + const pending = []; Object.keys(currentMap).forEach(selector => { try { const elements = document.querySelectorAll(selector); const animationType = currentMap[selector]; - + // Group elements by parent for proper stagger const elementsByParent = new Map(); elements.forEach(element => { @@ -76,38 +80,44 @@ } elementsByParent.get(parent).push(element); }); - - // Apply stagger within each parent group + elementsByParent.forEach(siblingElements => { siblingElements.forEach((element, index) => { - element.classList.add('ext-animate'); - element.dataset.extAnimate = animationType; - - const delay = Math.min(index * staggerDelay, maxStagger); - const duration = speeds[currentSpeed]; - - element.style.animationDuration = `${duration}s`; - element.style.animationDelay = `${delay}s`; - - // Clear stacking context after animation completes - element.addEventListener('animationend', () => { - element.dataset.extAnimated = 'true'; - }, { once: true }); - - // Elements already in viewport (like headers) - trigger immediately const rect = element.getBoundingClientRect(); - if (rect.top < window.innerHeight && rect.bottom > 0) { - requestAnimationFrame(() => { - element.classList.add(`ext-animated-${animationType}`); - }); - } - - observer.observe(element); - trackedElements.push(element); + pending.push({ + element, + animationType, + delay: Math.min(index * staggerDelay, maxStagger), + // Elements already in viewport (like headers) - reveal immediately + inViewport: rect.top < window.innerHeight && rect.bottom > 0, + }); }); }); } catch (e) {} }); + + // Batch all writes into one frame so layout happens once, not once per element. + const duration = speeds[currentSpeed]; + requestAnimationFrame(() => { + pending.forEach(({ element, animationType, delay, inViewport }) => { + element.classList.add('ext-animate'); + element.dataset.extAnimate = animationType; + element.style.animationDuration = `${duration}s`; + element.style.animationDelay = `${delay}s`; + + // Clear stacking context after animation completes + element.addEventListener('animationend', () => { + element.dataset.extAnimated = 'true'; + }, { once: true }); + + if (inViewport) { + element.classList.add(`ext-animated-${animationType}`); + } + + observer.observe(element); + trackedElements.push(element); + }); + }); } function resetAnimations() { @@ -129,19 +139,22 @@ }); requestAnimationFrame(() => { - trackedElements.forEach(element => { + const visible = trackedElements.map(element => { const rect = element.getBoundingClientRect(); - const isVisible = rect.top < window.innerHeight && rect.bottom > 0; - - if (isVisible) { - const animationType = element.dataset.extAnimate; - if (animationType) { - element.classList.add(`ext-animated-${animationType}`); - } + return rect.top < window.innerHeight && rect.bottom > 0; + }); + + trackedElements.forEach((element, index) => { + if (!visible[index]) { + return; + } + const animationType = element.dataset.extAnimate; + if (animationType) { + element.classList.add(`ext-animated-${animationType}`); } }); }); - + return true; } From bd6b972a3f66da70aba06ddfaef22bd307b376c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAmjad?= Date: Mon, 8 Jun 2026 13:07:12 +0500 Subject: [PATCH 2/2] reomve doc --- assets/js/animations-interactivity.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/assets/js/animations-interactivity.js b/assets/js/animations-interactivity.js index 184294ec..0fb069a2 100644 --- a/assets/js/animations-interactivity.js +++ b/assets/js/animations-interactivity.js @@ -59,16 +59,11 @@ trackedElements = []; - // Read every element's geometry before any class/style write. Interleaving - // getBoundingClientRect with writes forces a synchronous layout per element - // (layout thrash) - expensive on pages with many animated blocks. const pending = []; Object.keys(currentMap).forEach(selector => { try { const elements = document.querySelectorAll(selector); const animationType = currentMap[selector]; - - // Group elements by parent for proper stagger const elementsByParent = new Map(); elements.forEach(element => { if (element.classList.contains('ext-animate--off')) {