UNPKG

@sirhc77/canvas-math-kit

Version:

A lightweight, interactive canvas-based vector visualizer for math, linear algebra, and ML education. Built with React + TypeScript.

72 lines (71 loc) 2.98 kB
// hooks/usePointerDrag.ts import { useEffect, useRef } from 'react'; export function usePointerDrag(canvasRef, items, onChange, { origin, scale, snap, isLocked = false, hitRadius = 0.3, onDragStart = () => null, onDragEnd = () => null, }) { const dragIndexRef = useRef(null); const itemsRef = useRef(items); useEffect(() => { itemsRef.current = items; }, [items]); function getCanvasCoords(e) { const rect = canvasRef.current.getBoundingClientRect(); const mx = e.clientX - rect.left; const my = e.clientY - rect.top; const x = (mx - origin.x) / scale; const y = (origin.y - my) / scale; return { x, y }; } function isNear(p, v) { const dx = p.x - v.x; const dy = p.y - v.y; return dx * dx + dy * dy <= hitRadius * hitRadius; } function applySnap(x, y) { if (typeof snap === 'function') return snap(x, y); if (typeof snap === 'number' && snap > 0) { return [Math.round(x / snap) * snap, Math.round(y / snap) * snap]; } return [x, y]; } useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; const handleDown = (e) => { if (isLocked) return; const coords = getCanvasCoords(e); const index = items.findIndex((item) => item.draggable && isNear(coords, item)); if (index !== -1) { dragIndexRef.current = index; canvas.setPointerCapture(e.pointerId); onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(); } }; const handleMove = (e) => { if (isLocked) return; if (dragIndexRef.current === null) return; const coords = getCanvasCoords(e); const [x, y] = applySnap(coords.x, coords.y); const updated = itemsRef.current.map((item, i) => i === dragIndexRef.current ? Object.assign(Object.assign({}, item), { x, y }) : item); onChange(updated); }; const handleUp = (e) => { dragIndexRef.current = null; canvas.releasePointerCapture(e.pointerId); onDragEnd === null || onDragEnd === void 0 ? void 0 : onDragEnd(); }; canvas.addEventListener('pointerdown', handleDown); canvas.addEventListener('pointermove', handleMove); canvas.addEventListener('pointerup', handleUp); canvas.addEventListener('pointercancel', handleUp); return () => { canvas.removeEventListener('pointerdown', handleDown); canvas.removeEventListener('pointermove', handleMove); canvas.removeEventListener('pointerup', handleUp); canvas.removeEventListener('pointercancel', handleUp); }; }, [canvasRef, items, onChange, origin, scale, snap, isLocked, hitRadius]); }