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 100 101 102 103 | 19x 19x | import { PropsWithChildren, ReactElement, useEffect, useRef } from "react";
import css from "styled-jsx/css";
interface IScrollableText {
speedPxSeconds?: number;
delay?: number;
}
export const styles = css.global`
.ScrollableText-container {
position: relative;
overflow: hidden;
}
.ScrollableText-text {
white-space: nowrap;
width: 100%;
display: flex;
}
`;
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]);
return (
<div ref={ref} className="ScrollableText-container">
<div className="ScrollableText-text">{children}</div>
<style jsx>{styles}</style>
</div>
);
}
|