lightswind
Version:
A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.
81 lines (80 loc) • 3.67 kB
JavaScript
// @ts-nocheck
"use client";
import { jsx as _jsx } from "react/jsx-runtime";
import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { useRef } from "react";
import { cn } from "../../lib/utils";
export function ScrollCards({ cards, direction = "bottom", className, containerClassName, imageClassName, cardScale = 0.7, cardRotation = 5, }) {
const container = useRef(null);
const imageRefs = useRef([]);
useGSAP(() => {
gsap.registerPlugin(ScrollTrigger);
const imageElements = imageRefs.current.filter(Boolean);
const totalCards = imageElements.length;
if (!imageElements[0])
return;
gsap.set(imageElements[0], { x: "0%", y: "0%", scale: 1, rotation: 0 });
// Determine initial offset based on direction
const getInitialOffset = () => {
switch (direction) {
case "top":
return { y: "-100%", x: "0%" };
case "left":
return { x: "-100%", y: "0%" };
case "right":
return { x: "100%", y: "0%" };
case "bottom":
default:
return { y: "100%", x: "0%" };
}
};
for (let i = 1; i < totalCards; i++) {
gsap.set(imageElements[i], { ...getInitialOffset(), scale: 1, rotation: 0 });
}
const scrollTimeline = gsap.timeline({
scrollTrigger: {
trigger: container.current,
start: "top top",
end: `+=${window.innerHeight * totalCards}`,
pin: true,
scrub: 1,
pinSpacing: true,
anticipatePin: 1,
},
});
for (let i = 0; i < totalCards - 1; i++) {
const currentImage = imageElements[i];
const nextImage = imageElements[i + 1];
const position = i;
if (!currentImage || !nextImage)
continue;
scrollTimeline.to(currentImage, {
scale: cardScale,
rotation: cardRotation,
duration: 1,
ease: "power2.inOut",
}, position);
scrollTimeline.to(nextImage, {
x: "0%",
y: "0%",
duration: 1,
ease: "power2.inOut",
}, position);
}
const resizeObserver = new ResizeObserver(() => {
ScrollTrigger.refresh();
});
if (container.current) {
resizeObserver.observe(container.current);
}
return () => {
resizeObserver.disconnect();
scrollTimeline.kill();
};
}, { scope: container, dependencies: [direction, cards.length, cardScale, cardRotation] });
return (_jsx("div", { className: cn("relative w-full h-[70vh]", className), ref: container, children: _jsx("div", { className: "lightswind-scroll-cards-trigger relative flex h-[70vh] w-full items-center justify-center overflow-hidden p-4 lg:p-8", children: _jsx("div", { className: cn("relative w-[95%] sm:w-[90%] lg:w-[85%] max-w-[1400px] aspect-[4/3] md:aspect-video overflow-hidden rounded-3xl shadow-xl", containerClassName), children: cards.map((card, i) => (_jsx("img", { src: card.image, alt: card.alt || `Scroll card gallery image ${i}`, className: cn("absolute top-0 left-0 h-full w-full object-cover rounded-3xl will-change-transform shadow-2xl", imageClassName), ref: (el) => {
imageRefs.current[i] = el;
} }, card.id))) }) }) }));
}