80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useId, useState } from "react";
|
|
|
|
interface ScrollVelocityTextProps {
|
|
text: string;
|
|
direction: "left" | "right";
|
|
className?: string;
|
|
}
|
|
|
|
const GROUPS = [0, 1] as const;
|
|
|
|
export default function ScrollVelocityText({
|
|
text,
|
|
direction,
|
|
className = "",
|
|
}: ScrollVelocityTextProps) {
|
|
const trackId = useId();
|
|
const [isPaused, setIsPaused] = useState(false);
|
|
const [shouldReduceMotion, setShouldReduceMotion] = useState(
|
|
() =>
|
|
typeof window !== "undefined" &&
|
|
window.matchMedia("(prefers-reduced-motion: reduce)").matches,
|
|
);
|
|
|
|
useEffect(() => {
|
|
const media = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
const updatePreference = (event: MediaQueryListEvent) => {
|
|
setShouldReduceMotion(event.matches);
|
|
};
|
|
media.addEventListener("change", updatePreference);
|
|
return () => media.removeEventListener("change", updatePreference);
|
|
}, []);
|
|
|
|
const animationPaused = shouldReduceMotion || isPaused;
|
|
|
|
return (
|
|
<div className={`scroll-velocity-text ${className}`.trim()}>
|
|
<div className="scroll-velocity-viewport">
|
|
<div
|
|
id={trackId}
|
|
className={`scroll-velocity-track ${
|
|
direction === "left" ? "animate-marquee" : "animate-marquee-reverse"
|
|
}`}
|
|
style={{
|
|
animationPlayState: animationPaused ? "paused" : undefined,
|
|
}}
|
|
>
|
|
{GROUPS.map((group) => (
|
|
<span
|
|
key={group}
|
|
className="scroll-velocity-group"
|
|
aria-hidden={group === 0 ? undefined : "true"}
|
|
>
|
|
<span className="scroll-velocity-item">
|
|
<span aria-hidden={group === 0 ? "false" : "true"}>{text}</span>
|
|
<span aria-hidden="true" className="scroll-velocity-dot">
|
|
•
|
|
</span>
|
|
</span>
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{!shouldReduceMotion ? (
|
|
<button
|
|
type="button"
|
|
className="scroll-velocity-control"
|
|
aria-controls={trackId}
|
|
aria-pressed={isPaused}
|
|
onClick={() => setIsPaused((paused) => !paused)}
|
|
>
|
|
{isPaused ? "Retomar animação" : "Pausar animação"}
|
|
</button>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|