(function () { const CAROUSEL_CLASS = 'carousel-fade-gsap'; const FADE_DURATION = 800; function waitForDependencies(callback, tries = 0) { if ( window.gsap && window.ScrollTrigger && window.elementorFrontend ) { callback(); return; } if (tries < 100) { setTimeout(function () { waitForDependencies(callback, tries + 1); }, 100); } } /* * Reinicia las animaciones GSAP del slide activo. */ function restartGsapInSlide(slide) { if (!slide || !window.gsap || !window.ScrollTrigger) { return; } const containers = slide.querySelectorAll('.trps-container'); if (!containers.length) { return; } containers.forEach(function (root) { const title = root.querySelector('.trps-title'); if (!title) { return; } /* * Buscamos los elementos que el plugin ya convirtió * en palabras/letras. */ const units = root.querySelectorAll( '.trps-word, .trps-char' ); if (!units.length) { return; } /* * Eliminamos cualquier ScrollTrigger anterior * asociado a este título. */ ScrollTrigger.getAll().forEach(function (trigger) { if (trigger.trigger === root) { trigger.kill(); } }); /* * Leemos nuevamente las configuraciones * originales del widget. */ const stagger = parseFloat(root.dataset.stagger) || 0.06; const duration = parseFloat(root.dataset.duration) || 0.9; const distance = parseFloat(root.dataset.distance) || 100; const ease = root.dataset.ease || 'power3.out'; /* * Estado inicial. */ gsap.killTweensOf(units); gsap.set(units, { yPercent: distance, opacity: 0 }); /* * Ejecutamos nuevamente la animación. * * IMPORTANTE: * Aquí NO usamos ScrollTrigger. * * El Carousel ya nos está diciendo exactamente * cuándo el slide está visible. */ gsap.to(units, { yPercent: 0, opacity: 1, duration: duration, ease: ease, stagger: stagger, overwrite: 'auto' }); }); /* * Actualizamos ScrollTrigger después de que el * slide ya es visible. */ ScrollTrigger.refresh(); } /* * Inicializa un Carousel. */ function initCarousel(carousel) { if (carousel.dataset.fadeGsapReady === '1') { return; } const swiperElement = carousel.querySelector('.swiper'); if (!swiperElement) { return; } const swiper = swiperElement.swiper; if (!swiper) { return; } carousel.dataset.fadeGsapReady = '1'; /* * Cuando termina el cambio de slide, * esperamos a que termine nuestro fade CSS. */ swiper.on('slideChangeTransitionEnd', function () { setTimeout(function () { const activeSlide = swiper.slides[swiper.activeIndex]; restartGsapInSlide(activeSlide); }, 50); }); /* * Ejecutamos también la animación * del slide inicial. */ setTimeout(function () { const activeSlide = swiper.slides[swiper.activeIndex]; restartGsapInSlide(activeSlide); }, FADE_DURATION + 100); } /* * Busca todos los Carousels de Elementor. */ function scanCarousels() { document .querySelectorAll('.' + CAROUSEL_CLASS) .forEach(function (carousel) { initCarousel(carousel); }); } /* * Elementor puede crear el Carousel después de * cargar la página, por eso esperamos a que Elementor * termine de inicializar sus widgets. */ waitForDependencies(function () { if ( window.elementorFrontend && window.elementorFrontend.hooks ) { elementorFrontend.hooks.addAction( 'frontend/element_ready/widget', function () { setTimeout(scanCarousels, 300); } ); } /* * Primera búsqueda. */ setTimeout(scanCarousels, 500); /* * Segunda búsqueda por si Swiper tarda * un poco más en inicializarse. */ setTimeout(scanCarousels, 1500); /* * Último intento. */ setTimeout(scanCarousels, 3000); }); })();