useMousePosition
zyeon/use-mouse-positionFreeHook
A hook that tracks pointer position, optionally relative to an element, with clamped 0..1 normalized coordinates.
See it running
Open the demo on zyeon
ui.zyeon.ai/r/use-mouse-positionInstall it
npx shadcn@latest add https://ui.zyeon.ai/r/use-mouse-position.jsonSource
1"use client"23import * as React from "react"45export interface UseMousePositionOptions {6 /** 传了就额外给出相对该元素的坐标与 0..1 归一化值;不传则相对视口。 */7 ref?: React.RefObject<Element | null>8 /** 关掉监听(指针驱动的效果不在屏幕上时没必要跟踪)。默认 true。 */9 enabled?: boolean10 /** 用 requestAnimationFrame 把每帧的多次 pointermove 合并成一次提交。默认 true。 */11 throttleWithRaf?: boolean12}1314export interface MousePosition {15 /** 视口坐标(clientX / clientY)。 */16 x: number17 y: number18 /** 相对 `ref` 元素左上角的坐标;没传 ref 时等于视口坐标。 */19 elementX: number20 elementY: number21 /** `elementX/Y` 除以元素宽高并 clamp 到 0..1,直接可喂给渐变 / 视差 / 倾斜。 */22 normalizedX: number23 normalizedY: number24 /** 指针当前是否落在 `ref` 元素矩形内(没传 ref 时表示指针是否还在文档内)。 */25 isInside: boolean26}2728const INITIAL_POSITION: MousePosition = {29 x: 0,30 y: 0,31 elementX: 0,32 elementY: 0,33 normalizedX: 0,34 normalizedY: 0,35 isInside: false,36}3738function clamp01(value: number): number {39 if (Number.isNaN(value)) return 040 return Math.min(1, Math.max(0, value))41}4243/**44 * 跟踪指针位置,并可选地把它换算成相对某个元素的坐标 + 0..1 归一化值 ——45 * spotlight、磁吸按钮、视差、倾斜卡片这类效果要的就是后面那对归一化值。46 *47 * - **用 `pointermove` 而不是 `mousemove`**:鼠标、触控笔、触摸屏走同一套事件,48 * 不必再补一份 touch 分支。49 * - **每帧一次提交**:`pointermove` 一秒能打上百次,默认把最新坐标存进闭包变量、50 * 用 rAF 在下一帧统一 `setState`;`throttleWithRaf: false` 则逐事件同步提交。51 * - **归一化值恒在 0..1**:指针移出元素时 `isInside` 变 false,但归一化值被 clamp52 * 住不会越界,消费者不必自己兜边界;元素宽或高为 0 时按 0 处理,不会出 NaN。53 * - **SSR 安全**:监听全在 effect 内挂,首帧返回全 0 且 `isInside: false`。54 * - **清理**:pending 的 rAF、`pointermove` 与 `pointerleave` 监听在卸载 / 依赖55 * 变化时全部取消。56 */57export function useMousePosition(options: UseMousePositionOptions = {}): MousePosition {58 const { ref, enabled = true, throttleWithRaf = true } = options59 const [position, setPosition] = React.useState<MousePosition>(INITIAL_POSITION)6061 React.useEffect(() => {62// … truncated
Files it writes
More from zyeon
All 517 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| useAsyncuse-async | zyeon | Hooks | Free | no deps | |
| useBroadcastChanneluse-broadcast-channel | zyeon | Hooks | Free | no deps | |
| useClickOutsideuse-click-outside | zyeon | Hooks | Free | no deps | |
| useClipboardPasteuse-clipboard-paste | zyeon | Hooks | Free | no deps | |
| useControllableStateuse-controllable-state | zyeon | Hooks | Free | no deps | |
| useCopyToClipboarduse-copy-to-clipboard | zyeon | Hooks | Free | no deps | |
| useCountdownuse-countdown | zyeon | Hooks | Free | no deps | |
| useDebounceValueuse-debounce-value | zyeon | Hooks | Free | no deps |
