(() => { function initFooterLoop() { // Select all images that have animate-mask="true" but are not in SVGs with animate-mask const footerImages = [...document.querySelectorAll('svg:not([animate-mask]) image[animate-mask="true"]')]; if (!footerImages.length) return; const batchSize = 4; let animationCount = 0; // Utility function to shuffle array randomly function shuffleArray(array) { return array .map(value => ({ value, sort: Math.random() })) .sort((a, b) => a.sort - b.sort) .map(({ value }) => value); } // Reset path to its initial state function ensureInitialState(maskNumber) { document.querySelectorAll(`#morphPath_Footer${maskNumber}`).forEach(path => { const initialPath = path.getAttribute('d'); if (initialPath) { path.setAttribute('d', initialPath); } }); } // Main animation function for each image function animateImage(image) { // Get parent SVG and check if image is already animating const svg = image.closest('svg'); if (!svg || image.dataset.isAnimating === 'true') return; // Get clip-path URL and extract mask number const clipPathUrl = image.getAttribute('clip-path'); if (!clipPathUrl) return; const maskMatch = clipPathUrl.match(/Footer(\d+)/); if (!maskMatch) return; const maskNumber = maskMatch[1]; // Reset to initial state ensureInitialState(maskNumber); // Get required path elements const morphPath = svg.querySelector(`#morphPath_Footer${maskNumber}`); const originPath = svg.querySelector(`#pathOrigin_Footer${maskNumber}`); if (!morphPath || !originPath) return; // Handle first animation case animationCount++; if (animationCount === 1) { document.querySelectorAll('svg image[animate-mask="true"]').forEach(img => { const imgSvg = img.closest('svg'); if (!imgSvg) return; const imgClipPath = img.getAttribute('clip-path'); if (!imgClipPath) return; const imgMaskMatch = imgClipPath.match(/Footer(\d+)/); if (!imgMaskMatch) return; ensureInitialState(imgMaskMatch[1]); }); } // Get path data const targetPath = morphPath.getAttribute('d'); const originPathData = originPath.getAttribute('d'); // Validate path data if (!targetPath || !originPathData) return; // Set animating flag image.dataset.isAnimating = 'true'; // Create animation timeline gsap.timeline({ onComplete: () => { image.dataset.isAnimating = 'false'; morphPath.setAttribute('d', targetPath); } }) .to(morphPath, { duration: 4, ease: "power2.inOut", morphSVG: { shape: originPathData, type: "rotational" } }) .to(morphPath, { duration: 4, delay: 1, ease: "power2.inOut", morphSVG: { shape: targetPath, type: "rotational" } }); } // Function to animate a batch of images function animateBatch() { const availableImages = footerImages.filter(img => img.dataset.isAnimating !== 'true'); if (availableImages.length === 0) return; const batch = shuffleArray(availableImages).slice(0, batchSize); batch.forEach((image, i) => { setTimeout(() => animateImage(image), i * 200); }); setTimeout(animateBatch, 3000 + Math.random() * 2000); } // Initialize all images to their initial state document.querySelectorAll('svg image[animate-mask="true"]').forEach(image => { const clipPathUrl = image.getAttribute('clip-path'); if (!clipPathUrl) return; const maskMatch = clipPathUrl.match(/Footer(\d+)/); if (!maskMatch) return; ensureInitialState(maskMatch[1]); }); // Start the animation animateBatch(); } // Run on DOM load if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initFooterLoop); } else { initFooterLoop(); } })();