Parallax Slider

Slider
Scroll
Swiper
GSAP
JS
CMS
Last Updated: Nov 22, 2025
  • To disable scroll, set mousewheel: { enabled: false }
  • Update the animation on the slides and their children through the gsap timeline: tl.fromTo(item, { yPercent: -40 }, { yPercent: 0 }, "<");
  • To prevent the slides from snapping into view on release, set freeMode: { sticky: false }
  • Set section or page wrapper to overflow: clip to avoid unwanted horizontal scroll
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.css"/>
<script src="https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
	document.querySelectorAll(".slider-5_component").forEach((component) => {
		if (component.hasAttribute("data-slider-5")) return;
		component.setAttribute("data-slider-5", "");
    
		const sliderElement = component.querySelector(".slider-5_wrap");
		if (!sliderElement) return;

		const swiper = new Swiper(sliderElement, {
			slidesPerView: "auto",
			followFinger: true,
			loop: true,
			slideToClickedSlide: true,
			centeredSlides: true,
			freeMode: {
				enabled: true,
				minimumVelocity: 0,
				momentumRatio: 0.6,
				momentumVelocityRatio: 0.4,
				sticky: true
			},
			autoHeight: false,
			speed: 600,
			mousewheel: {
				enabled: true,
				forceToAxis: false,
				eventsTarget: document.body
			},
			keyboard: { enabled: true, onlyInViewport: true },
			navigation: {
				nextEl: component.querySelector(".slider-5_button.is-next"),
				prevEl: component.querySelector(".slider-5_button.is-prev"),
			},
			slideActiveClass: "is-active",
			slideDuplicateActiveClass: "is-active",
		});

		component.querySelectorAll(".slider-5_item").forEach((item) => {
			gsap.context(() => {
				let tl = gsap.timeline({ 
					paused: true,
					defaults: { ease: "none" }
				});
				tl.fromTo(".slider-5_image_element", { xPercent: -17 }, { xPercent: 0 });
				tl.fromTo(item, { yPercent: -40 }, { yPercent: 0 }, "<");
				tl.fromTo(item, { opacity: 0.1 }, { opacity: 1, ease: "power1.out" }, "<");
        
				tl.to(".slider-5_image_element", { xPercent: 17 });
				tl.to(item, { yPercent: 40 }, "<");
				tl.to(item, { opacity: 0.1, ease: "power1.in" }, "<");
        
				function animate() {
					const rect = item.getBoundingClientRect();
					const compRect = document.body.getBoundingClientRect();
					let progress = Math.max(0, Math.min(1, (compRect.right - rect.left) / (rect.width + compRect.width)));
					tl.progress(progress);
					requestAnimationFrame(animate);
				}
				requestAnimationFrame(animate);
			}, item);
		});
	});
});
</script>