Rearranged import orders for better visibility and readability. Also, cleaned up some of the typescript and JSX by adding appropriate line breaks and spaces, and ensuring the use of semicolons for better punctuation.
137 lines
3.3 KiB
TypeScript
137 lines
3.3 KiB
TypeScript
"use client";
|
|
import {
|
|
type MotionValue,
|
|
motion,
|
|
useScroll,
|
|
useSpring,
|
|
useTransform,
|
|
} from "framer-motion";
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import React from "react";
|
|
|
|
interface HeroParallaxProps {
|
|
products: product[];
|
|
children?: React.ReactNode;
|
|
}
|
|
|
|
interface product {
|
|
title: string;
|
|
link: string;
|
|
thumbnail: string;
|
|
}
|
|
|
|
export const HeroParallax = ({ products, children }: HeroParallaxProps) => {
|
|
const firstRow = products.slice(0, 5);
|
|
const secondRow = products.slice(5, 10);
|
|
const thirdRow = products.slice(10, 15);
|
|
const ref = React.useRef(null);
|
|
const { scrollYProgress } = useScroll({
|
|
target: ref,
|
|
offset: ["start start", "end start"],
|
|
});
|
|
|
|
const springConfig = { stiffness: 300, damping: 30, bounce: 100 };
|
|
|
|
const translateX = useSpring(
|
|
useTransform(scrollYProgress, [0, 1], [0, 1000]),
|
|
springConfig,
|
|
);
|
|
const translateXReverse = useSpring(
|
|
useTransform(scrollYProgress, [0, 1], [0, -1000]),
|
|
springConfig,
|
|
);
|
|
const rotateX = useSpring(
|
|
useTransform(scrollYProgress, [0, 0.2], [15, 0]),
|
|
springConfig,
|
|
);
|
|
const opacity = useSpring(
|
|
useTransform(scrollYProgress, [0, 0.2], [0.2, 1]),
|
|
springConfig,
|
|
);
|
|
const rotateZ = useSpring(
|
|
useTransform(scrollYProgress, [0, 0.2], [20, 0]),
|
|
springConfig,
|
|
);
|
|
const translateY = useSpring(
|
|
useTransform(scrollYProgress, [0, 0.2], [-700, 500]),
|
|
springConfig,
|
|
);
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className="h-[300vh] py-40 overflow-hidden antialiased relative flex flex-col self-auto [perspective:1000px] [transform-style:preserve-3d]"
|
|
>
|
|
{children}
|
|
<motion.div
|
|
style={{
|
|
rotateX,
|
|
rotateZ,
|
|
translateY,
|
|
opacity,
|
|
}}
|
|
className=""
|
|
>
|
|
<motion.div className="flex flex-row-reverse space-x-reverse space-x-20 mb-20">
|
|
{firstRow.map((product) => (
|
|
<ProductCard product={product} translate={translateX} key={product.title} />
|
|
))}
|
|
</motion.div>
|
|
<motion.div className="flex flex-row mb-20 space-x-20 ">
|
|
{secondRow.map((product) => (
|
|
<ProductCard
|
|
product={product}
|
|
translate={translateXReverse}
|
|
key={product.title}
|
|
/>
|
|
))}
|
|
</motion.div>
|
|
<motion.div className="flex flex-row-reverse space-x-reverse space-x-20">
|
|
{thirdRow.map((product) => (
|
|
<ProductCard product={product} translate={translateX} key={product.title} />
|
|
))}
|
|
</motion.div>
|
|
</motion.div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const ProductCard = ({
|
|
product,
|
|
translate,
|
|
}: {
|
|
product: {
|
|
title: string;
|
|
link: string;
|
|
thumbnail: string;
|
|
};
|
|
translate: MotionValue<number>;
|
|
}) => {
|
|
return (
|
|
<motion.div
|
|
style={{
|
|
x: translate,
|
|
}}
|
|
whileHover={{
|
|
y: -20,
|
|
}}
|
|
key={product.title}
|
|
className="group/product h-96 w-[30rem] relative flex-shrink-0"
|
|
>
|
|
<Link href={product.link} className="block group-hover/product:shadow-2xl ">
|
|
<Image
|
|
src={product.thumbnail}
|
|
height="600"
|
|
width="600"
|
|
className="object-cover object-left-top absolute h-full w-full rounded inset-0"
|
|
alt={product.title}
|
|
/>
|
|
</Link>
|
|
<div className="absolute inset-0 h-full w-full opacity-0 group-hover/product:opacity-80 bg-black pointer-events-none"></div>
|
|
<h2 className="absolute bottom-4 left-4 opacity-0 group-hover/product:opacity-100 text-white">
|
|
{product.title}
|
|
</h2>
|
|
</motion.div>
|
|
);
|
|
};
|