Marquee Effect
Marquee Effect é um componente de marquee para React e Tailwind CSS, da biblioteca Bund UI, com licença MIT. Copia e cola no teu projeto.
Bund UI
MIT
marquee
O que é
Marquee Effect é um componente de marquee para React e Tailwind CSS, da biblioteca Bund UI, com licença MIT. Copia e cola no teu projeto.
Como usar
Abre o componente no catálogo, carrega em copiar, e cola no teu projeto. O prompt copiado leva o código e as regras para o teu agente o reproduzir sem alterar nada.
Cinco cópias grátis por mês. Sem cartão.
Precisa de
npm i motion react-use-measure
Licença
Este componente vem de Bund UI e é redistribuído sob a licença MIT, que permite uso comercial. O aviso de copyright viaja com o código.
Marquee Effect
"use client";
import React from "react";
import { cn } from "@/lib/utils";
import { useMotionValue, animate, motion } from "motion/react";
import useMeasure from "react-use-measure";
export type InfiniteSliderProps = {
children: React.ReactNode;
gap?: number;
speed?: number;
speedOnHover?: number;
direction?: "horizontal" | "vertical";
reverse?: boolean;
className?: string;
};
export function MarqueeEffect({
children,
gap = 16,
speed = 100,
speedOnHover,
direction = "horizontal",
reverse = false,
className
}: InfiniteSliderProps) {
const [currentSpeed, setCurrentSpeed] = React.useState(speed);
const [ref, { width, height }] = useMeasure();
const translation = useMotionValue(0);
const [isTransitioning, setIsTransitioning] = React.useState(false);
const [key, setKey] = React.useState(0);
React.useEffect(() => {
let controls;
const size = direction === "horizontal" ? width : height;
const contentSize = size + gap;
const from = reverse ? -contentSize / 2 : 0;
const to = reverse ? 0 : -contentSize / 2;
const distanceToTravel = Math.abs(to - from);
const duration = distanceToTravel / currentSpeed;
if (isTransitioning) {
const remainingDistance = Math.abs(translation.get() - to);
const transitionDuration = remainingDistance / currentSpeed;
controls = animate(translation, [translation.get(), to], {
ease: "linear",
duration: transitionDuration,
onComplete: () => {
setIsTransitioning(false);
setKey((prevKey) => prevKey + 1);
}
});
} else {
controls = animate(translation, [from, to], {
ease: "linear",
duration: duration,
repeat: Infinity,
repeatType: "loop",
repeatDelay: 0,
onRepeat: () => {
translation.set(from);
}
});
}
return controls?.stop;
}, [key, translation, currentSpeed, width, height, gap, isTransitioning, direction, reverse]);
const hoverProps = speedOnHover
? {
onHoverStart: () => {
setIsTransitioning(true);
setCurrentSpeed(speedOnHover);
},
onHoverEnd: () => {
setIsTransitioning(true);
setCurrentSpeed(speed);
}
}
: {};
return (
<div className={cn("overflow-hidden", className)}>
<motion.div
className={cn("flex", { "w-max": direction !== "vertical" })}
style={{
...(direction === "horizontal" ? { x: translation } : { y: translation }),
gap: `${gap}px`,
flexDirection: direction === "horizontal" ? "row" : "column"
}}
ref={ref}
{...hoverProps}>
{children}
{children}
</motion.div>
</div>
);
}
import { MarqueeEffect } from "./marquee-effect";
const images = [
"/images/products/list1.png",
"/images/products/list2.png",
"/images/products/list3.png",
"/images/products/list4.png",
"/images/products/list5.png",
"/images/products/list6.png",
"/images/products/list7.png",
"/images/products/list8.png",
"/images/products/list9.png",
"/images/products/list10.png"
];
export default function MarqueeEffectExample() {
return (
<MarqueeEffect gap={24}>
{images.map((src, i) => (
<img
key={i}
src={src}
alt={`Product ${i + 1}`}
className="aspect-square w-32 rounded-md object-cover"
/>
))}
</MarqueeEffect>
);
}