Code:
Javascript/GSAP
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
<script src="https://unpkg.com/lenis@1.1.13/dist/lenis.min.js"></script>
<script>
gsap.registerPlugin(ScrollTrigger);
// Initialize Lenis smooth scrolling
const lenis = new Lenis();
lenis.on("scroll", ScrollTrigger.update);
gsap.ticker.add((time) => {
lenis.raf(time * 1000);
});
gsap.ticker.lagSmoothing(0);
const pinnedSection = document.querySelector(".pinned");
const pinnedHeight = window.innerHeight * 10; // Adjust based on your layout
const images = gsap.utils.toArray(".img");
function animateImageEntry(img) {
gsap.fromTo(
img,
{
scale: 1.25,
clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)",
opacity: 0,
},
{
scale: 1,
clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)",
opacity: 1,
duration: 1,
ease: "power2.inOut",
}
);
gsap.fromTo(
img.querySelector("img"),
{
filter: "contrast(2) brightness(10)",
},
{
filter: "contrast(1) brightness(1)",
duration: 1,
ease: "power2.inOut",
}
);
}
function animateImageExitForward(img) {
gsap.to(img, {
scale: 0.5,
opacity: 0,
duration: 1,
ease: "power2.inOut",
});
}
function animateImageExitReverse(img) {
gsap.to(img, {
scale: 1.25,
clipPath: "polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)",
duration: 1,
ease: "power2.inOut",
});
gsap.to(img.querySelector("img"), {
filter: "contrast(2) brightness(10)",
duration: 1,
ease: "power2.inOut",
});
}
// Start by animating the first image if it exists
if (images.length > 0) {
animateImageEntry(images[0]);
}
let lastCycle = 0;
ScrollTrigger.create({
trigger: pinnedSection,
start: "top top",
end: `+=${pinnedHeight} * 2`,
pin: true,
pinSpacing: true,
scrub: 0.1,
onUpdate: (self) => {
const totalProgress = self.progress * images.length; // Adjust for the number of images
const currentCycle = Math.floor(totalProgress);
const cycleProgress = (totalProgress % 1) * 100;
if (currentCycle < images.length) {
const currentImage = images[currentCycle];
const scale = 1 - (0.25 * cycleProgress) / 100;
gsap.to(currentImage, {
scale: scale,
duration: 0.1,
overwrite: "auto",
});
if (currentCycle !== lastCycle) {
if (self.direction > 0) {
if (lastCycle < images.length) animateImageExitForward(images[lastCycle]);
if (currentCycle < images.length) {
animateImageEntry(images[currentCycle]);
}
} else {
if (currentCycle < images.length) {
animateImageEntry(images[currentCycle]);
}
if (lastCycle < images.length) animateImageExitReverse(images[lastCycle]);
}
lastCycle = currentCycle;
}
}
},
});
</script>
CSS
.lenis.lenis-smooth {
scroll-behavior: auto !important;
}
.lenis.lenis-smooth [data-lenis-prevent] {
overscroll-behavior: contain;
}
.lenis.lenis-smooth iframe {
pointer-events: none;
}
.img {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(1.25);
width: 65%;
height: 90%;
clip-path: polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%);
}
.img img {
filter: contrast(1) brightness(1);
}
/* add this in the parent container or the container holding your images & video */
.pinned {
transition: 0s;
}