Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | 19x | import { PropsWithChildren, ReactElement, useEffect, useRef } from "react"; interface IScrollableText { speedPxSeconds?: number; delay?: number; } export default function ScrollableText({ children, speedPxSeconds = 20, delay = 5000, }: PropsWithChildren<IScrollableText>): ReactElement { const ref = useRef<HTMLDivElement>(null); useEffect(() => { let direction = -1; Iif (!ref.current) return; const container = ref.current; const text = container.firstElementChild as HTMLElement; const distanceToMove = -(text.scrollWidth - text.offsetWidth); let lastTime: number | undefined; let animationFrameId: number; let distanceMoved = 0; let delayedTimerId: NodeJS.Timeout | undefined; text.style.transform = "translateX(0px)"; Iif (text.scrollWidth <= container.offsetWidth) return; const update = (timestamp: number) => { const elapsedTime = lastTime ? timestamp - lastTime : 0; distanceMoved += (direction * speedPxSeconds * elapsedTime) / 1000; lastTime = timestamp; function setNewAnimationFrame(delay?: number) { Iif (!delay) { animationFrameId = requestAnimationFrame(update); return; } delayedTimerId = setTimeout(() => { lastTime = undefined; animationFrameId = requestAnimationFrame(update); }, delay); } function switchDirection() { direction = -direction; setNewAnimationFrame(delay); } Iif (distanceMoved < distanceToMove) { text.style.transform = `translateX(${distanceToMove}px)`; distanceMoved = distanceToMove; switchDirection(); return; } Iif (distanceMoved > 0) { text.style.transform = "translateX(0px)"; distanceMoved = 0; switchDirection(); return; } text.style.transform = `translateX(${distanceMoved}px)`; setNewAnimationFrame(); }; const initialTimerId = setTimeout(() => { lastTime = undefined; requestAnimationFrame(update); }, delay); return () => { clearTimeout(initialTimerId); cancelAnimationFrame(animationFrameId); Iif (delayedTimerId) clearTimeout(delayedTimerId); }; }, [speedPxSeconds, delay, children]); return ( <div ref={ref} className="container"> <div className="text">{children}</div> <style jsx>{` .container { position: relative; overflow: hidden; } .text { white-space: nowrap; width: 100%; display: flex; } `}</style> </div> ); } |