1 | import { PointerType } from '../types'
|
---|
2 | import { Vector2 } from '../types'
|
---|
3 |
|
---|
4 | const EVENT_TYPE_MAP: any = {
|
---|
5 | pointer: { start: 'down', change: 'move', end: 'up' },
|
---|
6 | mouse: { start: 'down', change: 'move', end: 'up' },
|
---|
7 | touch: { start: 'start', change: 'move', end: 'end' },
|
---|
8 | gesture: { start: 'start', change: 'change', end: 'end' }
|
---|
9 | }
|
---|
10 |
|
---|
11 | function capitalize(string: string) {
|
---|
12 | if (!string) return ''
|
---|
13 | return string[0].toUpperCase() + string.slice(1)
|
---|
14 | }
|
---|
15 |
|
---|
16 | const actionsWithoutCaptureSupported = ['enter', 'leave']
|
---|
17 |
|
---|
18 | function hasCapture(capture = false, actionKey: string) {
|
---|
19 | return capture && !actionsWithoutCaptureSupported.includes(actionKey)
|
---|
20 | }
|
---|
21 |
|
---|
22 | export function toHandlerProp(device: string, action = '', capture: boolean = false) {
|
---|
23 | const deviceProps = EVENT_TYPE_MAP[device]
|
---|
24 | const actionKey = deviceProps ? deviceProps[action] || action : action
|
---|
25 | return 'on' + capitalize(device) + capitalize(actionKey) + (hasCapture(capture, actionKey) ? 'Capture' : '')
|
---|
26 | }
|
---|
27 |
|
---|
28 | const pointerCaptureEvents = ['gotpointercapture', 'lostpointercapture']
|
---|
29 |
|
---|
30 | export function parseProp(prop: string) {
|
---|
31 | let eventKey = prop.substring(2).toLowerCase()
|
---|
32 | const passive = !!~eventKey.indexOf('passive')
|
---|
33 | if (passive) eventKey = eventKey.replace('passive', '')
|
---|
34 |
|
---|
35 | const captureKey = pointerCaptureEvents.includes(eventKey) ? 'capturecapture' : 'capture'
|
---|
36 | // capture = true
|
---|
37 | const capture = !!~eventKey.indexOf(captureKey)
|
---|
38 | // pointermovecapture => pointermove
|
---|
39 | if (capture) eventKey = eventKey.replace('capture', '')
|
---|
40 | return { device: eventKey, capture, passive }
|
---|
41 | }
|
---|
42 |
|
---|
43 | export function toDomEventType(device: string, action = '') {
|
---|
44 | const deviceProps = EVENT_TYPE_MAP[device]
|
---|
45 | const actionKey = deviceProps ? deviceProps[action] || action : action
|
---|
46 | return device + actionKey
|
---|
47 | }
|
---|
48 |
|
---|
49 | export function isTouch(event: UIEvent) {
|
---|
50 | return 'touches' in event
|
---|
51 | }
|
---|
52 |
|
---|
53 | export function getPointerType(event: UIEvent): PointerType {
|
---|
54 | if (isTouch(event)) return 'touch'
|
---|
55 | if ('pointerType' in event) return (event as PointerEvent).pointerType as PointerType
|
---|
56 | return 'mouse'
|
---|
57 | }
|
---|
58 |
|
---|
59 | function getCurrentTargetTouchList(event: TouchEvent) {
|
---|
60 | return Array.from(event.touches).filter(
|
---|
61 | (e) => e.target === event.currentTarget || (event.currentTarget as Node)?.contains?.(e.target as Node)
|
---|
62 | )
|
---|
63 | }
|
---|
64 |
|
---|
65 | function getTouchList(event: TouchEvent) {
|
---|
66 | return event.type === 'touchend' || event.type === 'touchcancel' ? event.changedTouches : event.targetTouches
|
---|
67 | }
|
---|
68 |
|
---|
69 | function getValueEvent<EventType extends TouchEvent | PointerEvent>(
|
---|
70 | event: EventType
|
---|
71 | ): EventType extends TouchEvent ? Touch : PointerEvent {
|
---|
72 | return (isTouch(event) ? getTouchList(event as TouchEvent)[0] : event) as any
|
---|
73 | }
|
---|
74 |
|
---|
75 | export function distanceAngle(P1: Touch | PointerEvent, P2: Touch | PointerEvent) {
|
---|
76 | // add a try catch
|
---|
77 | // attempt to fix https://github.com/pmndrs/use-gesture/issues/551
|
---|
78 | try {
|
---|
79 | const dx = P2.clientX - P1.clientX
|
---|
80 | const dy = P2.clientY - P1.clientY
|
---|
81 | const cx = (P2.clientX + P1.clientX) / 2
|
---|
82 | const cy = (P2.clientY + P1.clientY) / 2
|
---|
83 |
|
---|
84 | const distance = Math.hypot(dx, dy)
|
---|
85 | const angle = -(Math.atan2(dx, dy) * 180) / Math.PI
|
---|
86 | const origin = [cx, cy] as Vector2
|
---|
87 | return { angle, distance, origin }
|
---|
88 | } catch {}
|
---|
89 | return null
|
---|
90 | }
|
---|
91 |
|
---|
92 | export function touchIds(event: TouchEvent) {
|
---|
93 | return getCurrentTargetTouchList(event).map((touch) => touch.identifier)
|
---|
94 | }
|
---|
95 |
|
---|
96 | export function touchDistanceAngle(event: TouchEvent, ids: number[]) {
|
---|
97 | const [P1, P2] = Array.from(event.touches).filter((touch) => ids.includes(touch.identifier))
|
---|
98 | return distanceAngle(P1, P2)
|
---|
99 | }
|
---|
100 |
|
---|
101 | export function pointerId(event: PointerEvent | TouchEvent) {
|
---|
102 | const valueEvent = getValueEvent(event)
|
---|
103 | return isTouch(event) ? (valueEvent as Touch).identifier : (valueEvent as PointerEvent).pointerId
|
---|
104 | }
|
---|
105 |
|
---|
106 | export function pointerValues(event: PointerEvent | TouchEvent): Vector2 {
|
---|
107 | // if ('spaceX' in event) return [event.spaceX, event.spaceY]
|
---|
108 | const valueEvent = getValueEvent(event)
|
---|
109 | return [valueEvent.clientX, valueEvent.clientY]
|
---|
110 | }
|
---|
111 |
|
---|
112 | // wheel delta defaults from https://github.com/facebookarchive/fixed-data-table/blob/master/src/vendor_upstream/dom/normalizeWheel.js
|
---|
113 | const LINE_HEIGHT = 40
|
---|
114 | const PAGE_HEIGHT = 800
|
---|
115 |
|
---|
116 | export function wheelValues(event: WheelEvent): Vector2 {
|
---|
117 | let { deltaX, deltaY, deltaMode } = event
|
---|
118 | // normalize wheel values, especially for Firefox
|
---|
119 | if (deltaMode === 1) {
|
---|
120 | deltaX *= LINE_HEIGHT
|
---|
121 | deltaY *= LINE_HEIGHT
|
---|
122 | } else if (deltaMode === 2) {
|
---|
123 | deltaX *= PAGE_HEIGHT
|
---|
124 | deltaY *= PAGE_HEIGHT
|
---|
125 | }
|
---|
126 | return [deltaX, deltaY]
|
---|
127 | }
|
---|
128 |
|
---|
129 | export function scrollValues(event: UIEvent): Vector2 {
|
---|
130 | // If the currentTarget is the window then we return the scrollX/Y position.
|
---|
131 | // If not (ie the currentTarget is a DOM element), then we return scrollLeft/Top
|
---|
132 | const { scrollX, scrollY, scrollLeft, scrollTop } = event.currentTarget as Element & Window
|
---|
133 | return [scrollX ?? scrollLeft ?? 0, scrollY ?? scrollTop ?? 0]
|
---|
134 | }
|
---|
135 |
|
---|
136 | export function getEventDetails(event: any) {
|
---|
137 | const payload: any = {}
|
---|
138 | if ('buttons' in event) payload.buttons = event.buttons
|
---|
139 | if ('shiftKey' in event) {
|
---|
140 | const { shiftKey, altKey, metaKey, ctrlKey } = event
|
---|
141 | Object.assign(payload, { shiftKey, altKey, metaKey, ctrlKey })
|
---|
142 | }
|
---|
143 | return payload
|
---|
144 | }
|
---|