This website does not collect your personal information. The information you share with us is used only for educational purpose. While we provide links to other web sites, once you go to that page, you will be going to sites that are beyond our control and you are subject to the privacy policy of that site.
.main-image width: 100%; height: 100%; object-fit: contain; transition: transform 0.2s cubic-bezier(0.2, 0.9, 0.4, 1.1); transform-origin: center center; background: #fefefe; pointer-events: none; /* zoom handled by container overlay logic */
// ----- ZOOM EFFECT LOGIC (with GSAP smooth scaling & following mouse) ----- // We implement a "magnifying zoom" that follows the mouse cursor on container hover. // Instead of lens, we scale the image and translate based on relative mouse position. // This gives an elegant zoom effect similar to product zoom. let mouseX = 0.5, mouseY = 0.5; let isHovering = false; const ZOOM_FACTOR = 2.4; // 2.4x zoom // Update transform based on mouse position and scale function updateZoomTransform(scale, mouseRelX, mouseRelY) // Mouse move inside container: get relative coordinates (0 to 1) function onMouseMove(e) if (!isHovering) return; const rect = $zoomContainer[0].getBoundingClientRect(); let offsetX = e.clientX - rect.left; let offsetY = e.clientY - rect.top; let relX = Math.min(Math.max(offsetX / rect.width, 0), 1); let relY = Math.min(Math.max(offsetY / rect.height, 0), 1); mouseX = relX; mouseY = relY; if (currentZoomScale > 1.05) updateZoomTransform(currentZoomScale, mouseX, mouseY); // Enter zoom zone: animate scale up function onZoomEnter() { if (zoomTween) zoomTween.kill(); isHovering = true; // smooth zoom in with GSAP zoomTween = gsap.to({}, duration: 0.28, ease: "power2.out", onUpdate: function() const progress = this.progress(); // 0->1 currentZoomScale = 1 + (ZOOM_FACTOR - 1) * progress; updateZoomTransform(currentZoomScale, mouseX, mouseY); , onComplete: () => currentZoomScale = ZOOM_FACTOR; updateZoomTransform(currentZoomScale, mouseX, mouseY); ); } function onZoomLeave() { if (!isHovering) return; isHovering = false; if (zoomTween) zoomTween.kill(); // smooth zoom out zoomTween = gsap.to({}, duration: 0.25, ease: "power2.in", onUpdate: function() const progress = this.progress(); // 0->1 currentZoomScale = ZOOM_FACTOR * (1 - progress) + 1 * progress; updateZoomTransform(currentZoomScale, mouseX, mouseY); , onComplete: () => currentZoomScale = 1; updateZoomTransform(1, mouseX, mouseY); $mainImg.css('transform', 'scale(1) translate(0%, 0%)'); ); } // Attach zoom events $zoomContainer.on('mouseenter', onZoomEnter); $zoomContainer.on('mouseleave', onZoomLeave); $zoomContainer.on('mousemove', onMouseMove); // Prevent zoom container from flickering when leaving image edge $zoomContainer.css( cursor: 'zoom-in' ); // Optional: reset zoom on window resize or when active image changes we also reset // Also reset zoom state when image changes const originalSetActive = setActiveImage; window.setActiveImage = function(index) if (isHovering) onZoomLeave(); // force exit zoom gracefully originalSetActive(index); // after image loads, reset zoom transform and variables setTimeout(() => currentZoomScale = 1; $mainImg.css('transform', 'scale(1) translate(0%, 0%)'); isHovering = false; , 30); ; setActiveImage = function(index) if (isHovering) onZoomLeave(); originalSetActive(index); setTimeout(() => currentZoomScale = 1; $mainImg.css('transform', 'scale(1) translate(0%, 0%)'); isHovering = false; , 30); ; // Override global reference window.setActiveImage = setActiveImage; // ---------- SLIDER LOGIC (next/prev + infinite scroll with wrapping) ---------- function nextSlide() let newIndex = currentIndex + 1; if (newIndex >= galleryItems.length) newIndex = 0; setActiveImage(newIndex); function prevSlide() let newIndex = currentIndex - 1; if (newIndex < 0) newIndex = galleryItems.length - 1; setActiveImage(newIndex); // Attach events for slider buttons prevBtn.on('click', prevSlide); nextBtn.on('click', nextSlide); // Keyboard support (left/right arrows) $(document).on('keydown', function(e) if (e.key === 'ArrowLeft') prevSlide(); e.preventDefault(); else if (e.key === 'ArrowRight') nextSlide(); e.preventDefault(); else if (e.key === 'Escape') if (isHovering) onZoomLeave(); ); // Preload images for better experience function preloadImages() galleryItems.forEach(item => const img = new Image(); img.src = item.large; ); // Initialize everything function init() buildThumbnails(); // Set first main image large resolution $mainImg.attr('src', galleryItems[0].large); $mainImg.attr('alt', galleryItems[0].alt); currentIndex = 0; updateActiveThumbnail(); preloadImages(); // Reset any leftover transforms resetZoomWithGSAP(); // additional: ensure mouse leave cleans $zoomContainer.on('touchstart', function(e) // For mobile: disable zoom effect because hover not ideal, we fallback to tap and zoom? but still fine, just prevent weirdness // On touch devices, we don't want zoom to block, but still you can use slider. We'll disable zoom on touch to avoid half-state if (isHovering) onZoomLeave(); ); init(); // Add extra responsive handling: when window resets, recalc zoom boundaries let resizeTimer; $(window).on('resize', function() if (resizeTimer) clearTimeout(resizeTimer); resizeTimer = setTimeout(() => if (isHovering) // if zoom active, force smooth re-alignment on resize const rect = $zoomContainer[0].getBoundingClientRect(); // keep relative mouse within container if possible, but just update transform if (currentZoomScale > 1) updateZoomTransform(currentZoomScale, mouseX, mouseY); , 100); ); // tiny console info for codepen context console.log('✅ Product Thumbnail Slider with ZOOM effect | jQuery + GSAP | Active'); </script> </body> </html>
.nav-btn:hover background: #1e2f4b; color: white; transform: scale(0.96);
// Reset zoom animation to original function resetZoomWithGSAP() if (zoomTween) zoomTween.kill(); gsap.to($mainImg[0], duration: 0.3, scale: 1, transformOrigin: "center center", ease: "power2.out", overwrite: true, ); currentZoomScale = 1; isZooming = false;
.nav-buttons display: flex; gap: 0.6rem;
