Parallax Grid Scroll
A grid where two columns scroll in oposite directions, giving a parallax effect.
Installation
Install dependencies
npm i motion clsx tailwind-merge
Add util file
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Copy the source code
components/ui/parallax-scroll.tsx
"use client";
import { useScroll, useTransform } from "motion/react";
import { useRef } from "react";
import { motion } from "motion/react";
import Image from "next/image";
import { cn } from "@/lib/utils";
export const ParallaxScroll = ({
images,
className,
}: {
images: string[];
className?: string;
}) => {
const gridRef = useRef<any>(null);
const { scrollYProgress } = useScroll({
container: gridRef, // remove this if your container is not fixed height
offset: ["start start", "end start"], // remove this if your container is not fixed height
});
const translateFirst = useTransform(scrollYProgress, [0, 1], [0, -200]);
const translateSecond = useTransform(scrollYProgress, [0, 1], [0, 200]);
const translateThird = useTransform(scrollYProgress, [0, 1], [0, -200]);
const third = Math.ceil(images.length / 3);
const firstPart = images.slice(0, third);
const secondPart = images.slice(third, 2 * third);
const thirdPart = images.slice(2 * third);
return (
<div
className={cn("h-[40rem] items-start overflow-y-auto w-full", className)}
ref={gridRef}
>
<div
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 items-start max-w-5xl mx-auto gap-10 py-40 px-10"
ref={gridRef}
>
<div className="grid gap-10">
{firstPart.map((el, idx) => (
<motion.div
style={{ y: translateFirst }} // Apply the translateY motion value here
key={"grid-1" + idx}
>
<Image
src={el}
className="h-80 w-full object-cover object-left-top rounded-lg gap-10 !m-0 !p-0"
height="400"
width="400"
alt="thumbnail"
/>
</motion.div>
))}
</div>
<div className="grid gap-10">
{secondPart.map((el, idx) => (
<motion.div style={{ y: translateSecond }} key={"grid-2" + idx}>
<Image
src={el}
className="h-80 w-full object-cover object-left-top rounded-lg gap-10 !m-0 !p-0"
height="400"
width="400"
alt="thumbnail"
/>
</motion.div>
))}
</div>
<div className="grid gap-10">
{thirdPart.map((el, idx) => (
<motion.div style={{ y: translateThird }} key={"grid-3" + idx}>
<Image
src={el}
className="h-80 w-full object-cover object-left-top rounded-lg gap-10 !m-0 !p-0"
height="400"
width="400"
alt="thumbnail"
/>
</motion.div>
))}
</div>
</div>
</div>
);
};
Example
Copy the source code
components/ui/parallax-scroll-2.tsx
"use client";
import { useScroll, useTransform } from "motion/react";
import { useRef } from "react";
import { motion } from "motion/react";
import Image from "next/image";
import { cn } from "@/lib/utils";
export const ParallaxScrollSecond = ({
images,
className,
}: {
images: string[];
className?: string;
}) => {
const gridRef = useRef<any>(null);
const { scrollYProgress } = useScroll({
container: gridRef, // remove this if your container is not fixed height
offset: ["start start", "end start"], // remove this if your container is not fixed height
});
const translateYFirst = useTransform(scrollYProgress, [0, 1], [0, -200]);
const translateXFirst = useTransform(scrollYProgress, [0, 1], [0, -200]);
const rotateXFirst = useTransform(scrollYProgress, [0, 1], [0, -20]);
const translateYThird = useTransform(scrollYProgress, [0, 1], [0, -200]);
const translateXThird = useTransform(scrollYProgress, [0, 1], [0, 200]);
const rotateXThird = useTransform(scrollYProgress, [0, 1], [0, 20]);
const third = Math.ceil(images.length / 3);
const firstPart = images.slice(0, third);
const secondPart = images.slice(third, 2 * third);
const thirdPart = images.slice(2 * third);
return (
<div
className={cn("h-[40rem] items-start overflow-y-auto w-full", className)}
ref={gridRef}
>
<div
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 items-start max-w-5xl mx-auto gap-10 py-40 px-10"
ref={gridRef}
>
<div className="grid gap-10">
{firstPart.map((el, idx) => (
<motion.div
style={{
y: translateYFirst,
x: translateXFirst,
rotateZ: rotateXFirst,
}} // Apply the translateY motion value here
key={"grid-1" + idx}
>
<Image
src={el}
className="h-80 w-full object-cover object-left-top rounded-lg gap-10 !m-0 !p-0"
height="400"
width="400"
alt="thumbnail"
/>
</motion.div>
))}
</div>
<div className="grid gap-10">
{secondPart.map((el, idx) => (
<motion.div key={"grid-2" + idx}>
<Image
src={el}
className="h-80 w-full object-cover object-left-top rounded-lg gap-10 !m-0 !p-0"
height="400"
width="400"
alt="thumbnail"
/>
</motion.div>
))}
</div>
<div className="grid gap-10">
{thirdPart.map((el, idx) => (
<motion.div
style={{
y: translateYThird,
x: translateXThird,
rotateZ: rotateXThird,
}}
key={"grid-3" + idx}
>
<Image
src={el}
className="h-80 w-full object-cover object-left-top rounded-lg gap-10 !m-0 !p-0"
height="400"
width="400"
alt="thumbnail"
/>
</motion.div>
))}
</div>
</div>
</div>
);
};
Props
Prop name | Type | Description |
---|---|---|
className | string | The class name of the child component. |
images | string[] | The array of image URLs for the parallax scroll |
Build websites faster and 10x better than your competitors with Aceternity UI Pro
With the best in class components and templates, stand out from the crowd and get more attention to your website. Trusted by founders and entrepreneurs from all over the world.
The work that Manu did laid the foundation of online education that we provide today. The website he built for us is used by thousands of students every day. He took the requirements and built the ...
Jagvinder Kour
Chairperson at Golden Bells Academy