Files
Brain/brain-h5/src/mobile/MobileCursor.tsx
MingNian b0fce840a3 首次提交:脑晴测 H5 项目
包含前端源码、预构建产物(dist/)、零依赖 Node.js 服务器(server.mjs)、
Cloudflare Worker 版本(worker/)、部署文档(DEPLOY.md)和交接文档。

技术总监可直接 clone 后运行 node server.mjs 启动,无需安装 React 或 build。
DEEPSEEK_API_KEY 通过环境变量注入,未纳入仓库。
2026-07-28 10:24:47 +08:00

70 lines
2.1 KiB
TypeScript

import { useState, type PointerEvent as ReactPointerEvent } from "react";
type CursorState = {
visible: boolean;
active: boolean;
x: number;
y: number;
};
export function useMobileCursor() {
const debug =
typeof window !== "undefined" && new URLSearchParams(window.location.search).has("cursorDebug");
const [cursor, setCursor] = useState<CursorState>({
visible: false,
active: false,
x: 0,
y: 0,
});
const updatePosition = (event: ReactPointerEvent<HTMLElement>) => {
const bounds = event.currentTarget.getBoundingClientRect();
const localX = bounds.width === 0 ? 0 : ((event.clientX - bounds.left) / bounds.width) * event.currentTarget.offsetWidth;
const localY =
bounds.height === 0 ? 0 : ((event.clientY - bounds.top) / bounds.height) * event.currentTarget.offsetHeight;
setCursor((current) => ({
...current,
visible: true,
x: localX,
y: localY,
}));
};
return {
cursorHandlers: {
onPointerEnter: updatePosition,
onPointerMove: updatePosition,
onPointerDown: (event: ReactPointerEvent<HTMLElement>) => {
updatePosition(event);
setCursor((current) => ({ ...current, active: true }));
},
onPointerUp: (event: ReactPointerEvent<HTMLElement>) => {
updatePosition(event);
setCursor((current) => ({ ...current, active: false }));
},
onPointerCancel: () => {
setCursor((current) => ({ ...current, active: false, visible: false }));
},
onPointerLeave: () => {
setCursor((current) => ({ ...current, active: false, visible: false }));
},
},
cursorDebug: debug,
cursorElement: (
<div
className="mobile-cursor"
data-active={cursor.active ? "true" : "false"}
data-debug={debug ? "true" : "false"}
data-visible={cursor.visible ? "true" : "false"}
data-testid="mobile-cursor"
style={{
transform: `translate3d(${cursor.x}px, ${cursor.y}px, 0) translate(-50%, -50%)`,
}}
>
{debug ? <span className="mobile-cursor-hotspot" aria-hidden="true" /> : null}
</div>
),
};
}