"use client";
import { useRef } from "react";
import Image from "next/image";
import { motion, type MotionValue, useScroll, useTransform } from "framer-motion";
function HorizontalScrollItem({
image,
index,
isLeft,
scrollYProgress,
totalItems,
}: {
image: string;
index: number;
isLeft: boolean;
scrollYProgress: MotionValue<number>;
totalItems: number;
}) {
const direction = isLeft ? -1 : 1;
const position = index / totalItems;
const translateX = useTransform(
scrollYProgress,
[0, position, 1],
[-index * 500 * direction, 0, (totalItems - index) * 500 * direction]
);
const rotateY = useTransform(
scrollYProgress,
[0, position, 1],
[index * 35 * direction, 0, (totalItems - index) * -35 * direction]
);
const blur = useTransform(
scrollYProgress,
[0, position, 1],
[index * 2, 0, (totalItems - index) * 2]
);
const contrast = useTransform(
scrollYProgress,
[0, position, 1],
[index * 0.5, 1, (totalItems - index) * 0.5]
);
const translateY = useTransform(
scrollYProgress,
[0, position, 1],
[(totalItems - index) * 20, totalItems * 20, index * 20]
);
const filter = useTransform(
[blur, contrast],
([nextBlur, nextContrast]) =>
`blur(${nextBlur}px) contrast(${nextContrast})`
);
return (
<motion.div
style={{
translateX,
filter,
translateY: useTransform(translateY, (value) => `${value - 400}px`),
perspective: 1000,
transformStyle: "preserve-3d",
}}
className="absolute h-[300px] max-w-sm w-full overflow-visible rounded-xl"
>
<motion.div style={{ rotateY }}>
<Image
src={image}
alt={image}
width={1000}
height={1000}
className="h-[300px] w-full rounded-xl object-cover"
/>
</motion.div>
</motion.div>
);
}
export interface HorizontalScrollProps {
items: { image: string }[];
}
export function HorizontalScroll({ items }: HorizontalScrollProps) {
const ref = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
container: ref,
offset: ["start start", "end end"],
});
return (
<div
ref={ref}
className="relative w-full h-full overflow-y-auto"
style={{ minHeight: "400px" }}
>
<div className="absolute top-0 left-0 w-full h-full">
<div className="w-full" style={{ height: Math.max((items.length - 3) * 300, 300) }} />
</div>
<div className="sticky top-0 left-0 grid w-full h-full grid-cols-1 gap-12">
<div className="relative flex flex-col items-center justify-center w-full h-full">
{items.map((item, index) => (
<HorizontalScrollItem
key={`horizontal-scroll-item-${index}`}
image={item.image}
index={index}
isLeft
scrollYProgress={scrollYProgress}
totalItems={items.length}
/>
))}
</div>
</div>
</div>
);
}
export default HorizontalScroll;