Component that supports infinite scrolling, allowing for the seamless display of text, images, or videos
1
module.exports = {
theme: {
extend: {
animation: {
marquee: 'marquee var(--duration) linear infinite',
'marquee-vertical': 'marquee-vertical var(--duration) linear infinite',
},
keyframes: {
marquee: {
from: { transform: 'translateX(0)' },
to: { transform: 'translateX(calc(-100% - var(--gap)))' },
},
'marquee-vertical': {
from: { transform: 'translateY(0)' },
to: { transform: 'translateY(calc(-100% - var(--gap)))' },
},
},
},
},
}
2
import { cn } from "@/app/utils/cn";
interface MarqueeProps {
className?: string;
reverse?: boolean;
pauseOnHover?: boolean;
children?: React.ReactNode;
vertical?: boolean;
repeat?: number;
[key: string]: any;
}
export default function Marquee({
className,
reverse,
pauseOnHover = false,
children,
vertical = false,
repeat = 4,
...props
}: MarqueeProps) {
return (
<div
{...props}
className={cn(
"group flex overflow-hidden min-w-[260px] p-2 rotate-[12deg] [--duration:40s] [--gap:1rem] [gap:var(--gap)]",
{
"flex-row": !vertical,
"flex-col": vertical,
},
className
)}
>
{Array(repeat)
.fill(0)
.map((_, i) => (
<div
key={i}
className={cn(
"flex shrink-0 justify-around [gap:var(--gap)] w-full ",
{
"animate-marquee flex-row": !vertical,
"animate-marquee-vertical flex-col": vertical,
"group-hover:[animation-play-state:paused]": pauseOnHover,
"[animation-direction:reverse]": reverse,
}
)}
>
{children}
</div>
))}
</div>
);
}
3
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
4
npm install
5
npm run dev