import { useRef, useEffect, useState, useMemo, useId } from 'react'; import './CurvedLoop.css'; const CurvedLoop = ({ marqueeText = '1', speed = 2, className, curveAmount = 400, direction = 'left', interactive = true }) => { const text = useMemo(() => { const hasTrailing = /\s|\u00A0$/.test(marqueeText); return (hasTrailing ? marqueeText.replace(/\s+$/, '') : marqueeText) + '\u00A0'; }, [marqueeText]); const measureRef = useRef(null); const textPathRef = useRef(null); const pathRef = useRef(null); const [spacing, setSpacing] = useState(0); const [offset, setOffset] = useState(0); const uid = useId(); const pathId = `curve-${uid}`; const pathD = `M-100,40 Q500,${40 + curveAmount} 1540,40`; const dragRef = useRef(false); const lastXRef = useRef(0); const dirRef = useRef(direction); const velRef = useRef(0); const textLength = spacing; const totalText = textLength ? Array(Math.ceil(1800 / textLength) + 2) .fill(text) .join('') : text; const ready = spacing > 0; useEffect(() => { if (measureRef.current) setSpacing(measureRef.current.getComputedTextLength()); }, [text, className]); useEffect(() => { if (!spacing) return; if (textPathRef.current) { const initial = -spacing; textPathRef.current.setAttribute('startOffset', initial + 'px'); setOffset(initial); } }, [spacing]); useEffect(() => { if (!spacing || !ready) return; let frame = 0; const step = () => { if (!dragRef.current && textPathRef.current) { const delta = dirRef.current === 'right' ? speed : -speed; const currentOffset = parseFloat(textPathRef.current.getAttribute('startOffset') || '0'); let newOffset = currentOffset + delta; const wrapPoint = spacing; if (newOffset <= -wrapPoint) newOffset += wrapPoint; if (newOffset > 0) newOffset -= wrapPoint; textPathRef.current.setAttribute('startOffset', newOffset + 'px'); setOffset(newOffset); } frame = requestAnimationFrame(step); }; frame = requestAnimationFrame(step); return () => cancelAnimationFrame(frame); }, [spacing, speed, ready]); const onPointerDown = e => { if (!interactive) return; dragRef.current = true; lastXRef.current = e.clientX; velRef.current = 0; e.target.setPointerCapture(e.pointerId); }; const onPointerMove = e => { if (!interactive || !dragRef.current || !textPathRef.current) return; const dx = e.clientX - lastXRef.current; lastXRef.current = e.clientX; velRef.current = dx; const currentOffset = parseFloat(textPathRef.current.getAttribute('startOffset') || '0'); let newOffset = currentOffset + dx; const wrapPoint = spacing; if (newOffset <= -wrapPoint) newOffset += wrapPoint; if (newOffset > 0) newOffset -= wrapPoint; textPathRef.current.setAttribute('startOffset', newOffset + 'px'); setOffset(newOffset); }; const endDrag = () => { if (!interactive) return; dragRef.current = false; dirRef.current = velRef.current > 0 ? 'right' : 'left'; }; const cursorStyle = interactive ? (dragRef.current ? 'grabbing' : 'grab') : 'auto'; return (
{text} {ready && ( {totalText} )}
); }; export default CurvedLoop;
Åpningstider Fre 10.00 – 19.00 | Lør 10.00 – 19.00 | Søn 10.00 – 17.00

Ta toget til Oslo Motor Show

Vi oppfordrer våre besøkende til å ta toget til NOVA Spektrum i forbindelse med årets store høydepunkt for alle som elsker motor og kjøretøy. Bruk av

Les mer »

Vinnerne fra Oslo Motor Show 2024

Juryns Val Amcar Ford Thunderbird 1959, Joakim Sandberg, Vendelsö, SverigeFord Fairlane 500 Sunliner 1959, Runar Hannestad, Borgenhaugen, NorgeChevrolet Impala 1967, Peer Tarkington, Sandvika, NorgeDodge Charger

Les mer »

Møt “Tidsjegerne”!

Drifterne som vi tidligere har skrevet om, de kommer for å lage show og røyk. “Tidsjegerne” kommer for å vinne! Disse mener alvor, og her

Les mer »

Sublicity er tilbake!

Det tok virkelig tid før Per Øivind Martinsens byggverk sto klart. Den slanke Ford Roadsteren, Sublicity, begynte sin reise i Norge for over 40 år

Les mer »

Et legendarisk custombygg

Bosse “Gamen” Sandbergs Wild Bird er et legendarisk custombygg som opprinnelig ble bygd i 1965. Bilen forsvant på slutten av 1960-tallet, men ble gjenopplivet flere

Les mer »