| 1 | type Point = { x: number; y: number } |
| 2 | |
| 3 | export function createAim(props: { |
| 4 | enabled: () => boolean |
| 5 | active: () => string | undefined |
| 6 | el: () => HTMLElement | undefined |
| 7 | onActivate: (id: string) => void |
| 8 | delay?: number |
| 9 | max?: number |
| 10 | tolerance?: number |
| 11 | edge?: number |
| 12 | }) { |
| 13 | const state = { |
| 14 | locs: [] as Point[], |
| 15 | timer: undefined as number | undefined, |
| 16 | pending: undefined as string | undefined, |
| 17 | over: undefined as string | undefined, |
| 18 | last: undefined as Point | undefined, |
| 19 | } |
| 20 | |
| 21 | const delay = props.delay ?? 250 |
| 22 | const max = props.max ?? 4 |
| 23 | const tolerance = props.tolerance ?? 80 |
| 24 | const edge = props.edge ?? 18 |
| 25 | |
| 26 | const cancel = () => { |
| 27 | if (state.timer !== undefined) clearTimeout(state.timer) |
| 28 | state.timer = undefined |
| 29 | state.pending = undefined |
| 30 | } |
| 31 | |
| 32 | const reset = () => { |
| 33 | cancel() |
| 34 | state.over = undefined |
| 35 | state.last = undefined |
| 36 | state.locs.length = 0 |
| 37 | } |
| 38 | |
| 39 | const move = (event: MouseEvent) => { |
| 40 | if (!props.enabled()) return |
| 41 | const el = props.el() |
| 42 | if (!el) return |
| 43 | |
| 44 | const rect = el.getBoundingClientRect() |
| 45 | const x = event.clientX |
| 46 | const y = event.clientY |
| 47 | if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) return |
| 48 | |
| 49 | state.locs.push({ x, y }) |
| 50 | if (state.locs.length > max) state.locs.shift() |
| 51 | } |
| 52 | |
| 53 | const wait = () => { |
| 54 | if (!props.enabled()) return 0 |
| 55 | if (!props.active()) return 0 |
| 56 | |
| 57 | const el = props.el() |
| 58 | if (!el) return 0 |
| 59 | if (state.locs.length < 2) return 0 |
| 60 | |