import { type MutableRefObject } from 'react'; /** * THE marquee animation core (single source of truth) — extracted verbatim * from so every endless-scroll surface (card strips, logo strips, * quick-action walls, chip marquees) shares ONE rAF position engine ("unify * means delete variations"). * * The engine is axis- and driver-agnostic: it owns a FLOAT position ref, a * velocity envelope, seam wrapping, and an optional chevron-style glide; the * caller decides what a position "means" via `apply` — writing * `scroller.scrollLeft` (CardsStrip: a real scroller the user can also drag) * or a `translate` transform (MarqueeWall: decorative tracks, incl. vertical — * mandatory inside surfaces that forbid inner scrollers, e.g. the deck's * mobile single-fold panels). * * Engine semantics (do not "simplify"): * - Position lives in a FLOAT ref, never read-modify-write from the DOM: * scrollLeft reads back rounded values, so sub-pixel increments * (speed/fps < 1px) get eaten by rounding — stutter + measurably slower * than the configured speed. * - Velocity envelope (~250ms time constant) eases between 0 and `speed` * instead of binary stop/start — pause decelerates, resume accelerates from * the current position (GSAP-marquee behavior; hard cuts read as jank). * - Glide consumes a signed remaining-distance, wrapping modulo the copy size * each frame, so seam warps are invisible to it (browser smooth-scroll * animates toward an ABSOLUTE target and lunges after a warp). * - `isPaused` is evaluated every frame — the caller owns the pause-reason * set (hover, viewport, tab visibility, suppress windows); the engine only * turns it into a smooth velocity target. */ export interface MarqueeEngineOptions { /** Master switch — the rAF runs only while true. On (re)activation the * position resyncs from `readBack` when provided. */ active: boolean; /** Cruise speed in px/s. */ speed: number; /** Per-frame pause verdict (caller-owned reason set). `now` is the rAF * timestamp, for suppress-until comparisons. */ isPaused: (now: number) => boolean; /** Single-copy size in px (the wrap modulus). Return <= 0 to disable * wrapping (position advances unwrapped — callers should size tracks so * this never happens while active). */ getWrapSize: () => number; /** Lower bound of the wrap range: positions stay in * [wrapMin, wrapMin + wrapSize). Default 0. Scroller drivers with USER * scrolling pass their seam-warp buffer here so engine-written positions * never enter the re-center zones — an exact-edge warp check is racy * against the rAF (the engine nudges the scroller off the edge before the * scroll handler samples it → "stuck at the left edge"). */ getWrapMin?: () => number; /** Write the position to the DOM (scrollLeft assignment or transform). */ apply: (pos: number) => void; /** Read the externally-mutated position back (real scrollers: user drag / * native smooth scroll move scrollLeft under the engine). Omit for * transform drivers — nothing external can move them. */ readBack?: () => number; /** Called at the end of every frame (e.g. hover re-resolution after the * track moved under a stationary pointer). */ onAfterFrame?: () => void; } export interface MarqueeEngineHandle { /** The engine's float position (px along the axis, 0 ≤ pos < wrap while * wrapping). Callers may write it together with their own DOM write (seam * warp on manual scroll). */ posRef: MutableRefObject; /** Accumulate a signed glide distance (chevron navigation). Runs through * the engine's own tween — never native smooth scroll while wrapping. */ glideBy: (px: number) => void; /** Wrap an arbitrary position into [0, wrapSize). */ wrap: (pos: number) => number; } export declare function useMarqueeEngine(options: MarqueeEngineOptions): MarqueeEngineHandle; //# sourceMappingURL=use-marquee-engine.d.ts.map