Retour au catalogue
Marquee Dual Speed
Deux rangees de marquee a vitesses et directions differentes creant un effet de profondeur visuelle.
marqueemedium Both Responsive a11y
boldplayfuluniversalagencyeventstacked
Theme
"use client";
import { motion } from "framer-motion";
interface MarqueeDualSpeedProps {
topItems?: string[];
bottomItems?: string[];
topSpeed?: number;
bottomSpeed?: number;
}
function MarqueeRow({
items,
speed,
reverse = false,
style,
}: {
items: string[];
speed: number;
reverse?: boolean;
style?: React.CSSProperties;
}) {
const doubled = [...items, ...items];
return (
<div style={{ overflow: "hidden", ...style }}>
<motion.div
animate={{ x: reverse ? ["-50%", "0%"] : ["0%", "-50%"] }}
transition={{ duration: speed, ease: "linear", repeat: Infinity }}
style={{ display: "flex", whiteSpace: "nowrap" }}
>
{doubled.map((item, i) => (
<span
key={`${item}-${i}`}
style={{
display: "inline-flex",
alignItems: "center",
flexShrink: 0,
paddingRight: "2.5rem",
fontSize: "clamp(1.25rem, 3vw, 2.5rem)",
fontWeight: 800,
letterSpacing: "-0.02em",
textTransform: "uppercase",
color: "var(--color-foreground)",
}}
>
{item}
</span>
))}
</motion.div>
</div>
);
}
export default function MarqueeDualSpeed({
topItems = [],
bottomItems = [],
topSpeed = 25,
bottomSpeed = 35,
}: MarqueeDualSpeedProps) {
if (topItems.length === 0 && bottomItems.length === 0) return null;
return (
<section
style={{
padding: "2rem 0",
background: "var(--color-background)",
overflow: "hidden",
}}
>
{topItems.length > 0 && (
<MarqueeRow
items={topItems}
speed={topSpeed}
style={{ marginBottom: "0.75rem" }}
/>
)}
{bottomItems.length > 0 && (
<MarqueeRow
items={bottomItems}
speed={bottomSpeed}
reverse
style={{
opacity: 0.35,
}}
/>
)}
</section>
);
}