Loading

Product Thumbnail Slider With Zoom Effect Jquery Codepen Apr 2026

// ----- 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) if (!isHovering // 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>

.thumb-img width: 100%; height: 100%; object-fit: cover; display: block;

.thumbnail-track-wrapper::-webkit-scrollbar height: 6px; product thumbnail slider with zoom effect jquery codepen

<div class="product-showcase"> <div class="product-grid"> <!-- Zoomable main area --> <div class="zoom-container" id="zoomContainer"> <img class="main-image" id="mainImage" src="https://picsum.photos/id/20/800/600" alt="Product main view"> <div class="zoom-badge"> <i class="fas fa-search-plus"></i> Hover to zoom </div> </div>

/* Thumbnail slider area */ .slider-section margin-top: 0.5rem; // ----- ZOOM EFFECT LOGIC (with GSAP smooth

.slider-title font-weight: 600; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 1px; color: #2c3e66; background: rgba(255,255,240,0.6); padding: 0.2rem 0.8rem; border-radius: 40px;

// DOM elements const $mainImg = $('#mainImage'); const $zoomContainer = $('#zoomContainer'); const $thumbTrack = $('#thumbTrack'); const $thumbWrapper = $('#thumbWrapper'); const prevBtn = $('#prevThumbBtn'); const nextBtn = $('#nextThumbBtn'); let mouseX = 0

/* main product card container */ .product-showcase max-width: 1280px; width: 100%; background: rgba(255,255,255,0.75); backdrop-filter: blur(2px); border-radius: 2.5rem; box-shadow: 0 25px 45px -12px rgba(0,0,0,0.25), 0 4px 12px rgba(0,0,0,0.05); padding: 2rem 2rem 2rem 2rem; transition: all 0.2s ease;

body background: linear-gradient(145deg, #f5f7fc 0%, #eef2f6 100%); font-family: 'Inter', sans-serif; min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 2rem;

.nav-btn background: white; border: none; width: 38px; height: 38px; border-radius: 60px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); cursor: pointer; transition: all 0.2s; color: #1f2b48; font-size: 1rem;

Loading
Download a 360º Photo