GSAP - ScrollTrigger
by Hugo Vale Pereira
HTML
<!--Tutorial from: https://www.youtube.com/watch?v=Gy9IS0Y6dy0 Nov 2025
inspired from: https://www.wildyriftian.com/works
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/ScrollTrigger.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/ScrollToPlugin.min.js"></script>
<!-- <nav>
<p>Tutorial</p>
<p>GSAP</p>
</nav> -->
<div class="panel">Panel 1</div>
<div class="panel">Panel 2</div>
<div class="panel">Panel 3</div>
<div class="panel">Panel 4</div>
<div class="panel">Panel 5</div>
CSS
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Arial", sans-serif;
}
.panel {
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-size: 3rem;
font-weight: bold;
color: white;
}
.panel:nth-of-type(1) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.panel:nth-of-type(2) {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.panel:nth-of-type(3) {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.panel:nth-of-type(4) {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
.panel:nth-of-type(5) {
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
}
JavaScript
// gsap.registerPlugin(ScrollTrigger);
const panels = gsap.utils.toArray(".panel");
let isUserInteracting = false;
let snapTimeout;
// Detect when user starts interacting
window.addEventListener(
"wheel",
() => {
console.log("wheel event");
isUserInteracting = true;
clearTimeout(snapTimeout);
},
{ passive: true },
);
window.addEventListener(
"touchstart",
() => {
console.log("touchstart event");
isUserInteracting = true;
clearTimeout(snapTimeout);
},
{ passive: true },
);
window.addEventListener("mousedown", () => {
console.log("mousedown event");
isUserInteracting = true;
clearTimeout(snapTimeout);
});
// Detect when user stops interacting
// window.addEventListener("wheel", handleInteractionEnd, { passive: true });
// window.addEventListener("touchend", handleInteractionEnd);
// window.addEventListener("mouseup", handleInteractionEnd);
window.addEventListener("scrollend", handleInteractionEnd);
//Polyfill for if the Browser doesn't support 'scrollend' event (Safari)
/* if ('onscrollend' in window) {
document.onscrollend = callback
} else {
document.onscroll = event => {
clearTimeout(window.scrollEndTimer)
window.scrollEndTimer = setTimeout(callback, 100)
}
} */
function handleInteractionEnd() {
console.log("scrollend event");
clearTimeout(snapTimeout);
snapTimeout = setTimeout(() => {
isUserInteracting = false;
snapToNearest();
}, 50); // Small delay to detect end of interaction
}
function snapToNearest() {
const scrollProgress =
window.scrollY /
(document.documentElement.scrollHeight - window.innerHeight);
const targetPanel = Math.round(scrollProgress * (panels.length - 1));
const targetScroll =
(targetPanel / (panels.length - 1)) *
(document.documentElement.scrollHeight - window.innerHeight);
gsap.to(window, {
scrollTo: targetScroll,
duration: 0.5,
ease: "power2.inOut",
...