You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

inputs.ts 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. import type React from 'react'
  2. import type { TLKeyboardInfo, TLPointerInfo } from './types'
  3. import { Vec, Utils } from './utils'
  4. const DOUBLE_CLICK_DURATION = 250
  5. class Inputs {
  6. pointer?: TLPointerInfo<string>
  7. keyboard?: TLKeyboardInfo
  8. keys: Record<string, boolean> = {}
  9. pointerUpTime = 0
  10. touchStart<T extends string>(e: TouchEvent | React.TouchEvent, target: T): TLPointerInfo<T> {
  11. const { shiftKey, ctrlKey, metaKey, altKey } = e
  12. e.preventDefault()
  13. const touch = e.changedTouches[0]
  14. const info: TLPointerInfo<T> = {
  15. target,
  16. pointerId: touch.identifier,
  17. origin: Inputs.getPoint(touch),
  18. delta: [0, 0],
  19. point: Inputs.getPoint(touch),
  20. pressure: Inputs.getPressure(touch),
  21. shiftKey,
  22. ctrlKey,
  23. metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
  24. altKey,
  25. }
  26. this.pointer = info
  27. return info
  28. }
  29. touchMove<T extends string>(e: TouchEvent | React.TouchEvent, target: T): TLPointerInfo<T> {
  30. const { shiftKey, ctrlKey, metaKey, altKey } = e
  31. e.preventDefault()
  32. const touch = e.changedTouches[0]
  33. const prev = this.pointer
  34. const point = Inputs.getPoint(touch)
  35. const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0]
  36. const info: TLPointerInfo<T> = {
  37. origin: point,
  38. ...prev,
  39. target,
  40. pointerId: touch.identifier,
  41. point,
  42. delta,
  43. pressure: Inputs.getPressure(touch),
  44. shiftKey,
  45. ctrlKey,
  46. metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
  47. altKey,
  48. }
  49. this.pointer = info
  50. return info
  51. }
  52. pointerDown<T extends string>(e: PointerEvent | React.PointerEvent, target: T): TLPointerInfo<T> {
  53. const { shiftKey, ctrlKey, metaKey, altKey } = e
  54. const point = Inputs.getPoint(e)
  55. const info: TLPointerInfo<T> = {
  56. target,
  57. pointerId: e.pointerId,
  58. origin: point,
  59. point: point,
  60. delta: [0, 0],
  61. pressure: Inputs.getPressure(e),
  62. shiftKey,
  63. ctrlKey,
  64. metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
  65. altKey,
  66. }
  67. this.pointer = info
  68. return info
  69. }
  70. pointerEnter<T extends string>(
  71. e: PointerEvent | React.PointerEvent,
  72. target: T
  73. ): TLPointerInfo<T> {
  74. const { shiftKey, ctrlKey, metaKey, altKey } = e
  75. const point = Inputs.getPoint(e)
  76. const info: TLPointerInfo<T> = {
  77. target,
  78. pointerId: e.pointerId,
  79. origin: point,
  80. delta: [0, 0],
  81. point: point,
  82. pressure: Inputs.getPressure(e),
  83. shiftKey,
  84. ctrlKey,
  85. metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
  86. altKey,
  87. }
  88. this.pointer = info
  89. return info
  90. }
  91. pointerMove<T extends string>(e: PointerEvent | React.PointerEvent, target: T): TLPointerInfo<T> {
  92. const { shiftKey, ctrlKey, metaKey, altKey } = e
  93. const prev = this.pointer
  94. const point = Inputs.getPoint(e)
  95. const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0]
  96. const info: TLPointerInfo<T> = {
  97. origin: point,
  98. ...prev,
  99. target,
  100. pointerId: e.pointerId,
  101. point,
  102. delta,
  103. pressure: Inputs.getPressure(e),
  104. shiftKey,
  105. ctrlKey,
  106. metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
  107. altKey,
  108. }
  109. this.pointer = info
  110. return info
  111. }
  112. pointerUp<T extends string>(e: PointerEvent | React.PointerEvent, target: T): TLPointerInfo<T> {
  113. const { shiftKey, ctrlKey, metaKey, altKey } = e
  114. const prev = this.pointer
  115. const point = Inputs.getPoint(e)
  116. const delta = prev?.point ? Vec.sub(point, prev.point) : [0, 0]
  117. const info: TLPointerInfo<T> = {
  118. origin: point,
  119. ...prev,
  120. target,
  121. pointerId: e.pointerId,
  122. point,
  123. delta,
  124. pressure: Inputs.getPressure(e),
  125. shiftKey,
  126. ctrlKey,
  127. metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
  128. altKey,
  129. }
  130. this.pointer = info
  131. this.pointerUpTime = Date.now()
  132. return info
  133. }
  134. panStart = (e: WheelEvent): TLPointerInfo<'wheel'> => {
  135. const { shiftKey, ctrlKey, metaKey, altKey } = e
  136. const info: TLPointerInfo<'wheel'> = {
  137. target: 'wheel',
  138. pointerId: this.pointer?.pointerId || 0,
  139. origin: this.pointer?.origin || [0, 0],
  140. delta: [0, 0],
  141. pressure: 0.5,
  142. point: Inputs.getPoint(e),
  143. shiftKey,
  144. ctrlKey,
  145. metaKey,
  146. altKey,
  147. }
  148. this.pointer = info
  149. return info
  150. }
  151. pan = (delta: number[], e: WheelEvent): TLPointerInfo<'wheel'> => {
  152. if (!this.pointer || this.pointer.target !== 'wheel') {
  153. return this.panStart(e)
  154. }
  155. const { shiftKey, ctrlKey, metaKey, altKey } = e
  156. const prev = this.pointer
  157. const point = Inputs.getPoint(e)
  158. const info: TLPointerInfo<'wheel'> = {
  159. ...prev,
  160. target: 'wheel',
  161. delta,
  162. point,
  163. shiftKey,
  164. ctrlKey,
  165. metaKey,
  166. altKey,
  167. }
  168. this.pointer = info
  169. return info
  170. }
  171. isDoubleClick() {
  172. if (!this.pointer) return false
  173. const { origin, point } = this.pointer
  174. return Date.now() - this.pointerUpTime < DOUBLE_CLICK_DURATION && Vec.dist(origin, point) < 4
  175. }
  176. clear() {
  177. this.pointer = undefined
  178. }
  179. resetDoubleClick() {
  180. this.pointerUpTime = 0
  181. }
  182. keydown = (e: KeyboardEvent | React.KeyboardEvent): TLKeyboardInfo => {
  183. const { shiftKey, ctrlKey, metaKey, altKey } = e
  184. this.keys[e.key] = true
  185. return {
  186. point: this.pointer?.point || [0, 0],
  187. origin: this.pointer?.origin || [0, 0],
  188. key: e.key,
  189. keys: Object.keys(this.keys),
  190. shiftKey,
  191. ctrlKey,
  192. metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
  193. altKey,
  194. }
  195. }
  196. keyup = (e: KeyboardEvent | React.KeyboardEvent): TLKeyboardInfo => {
  197. const { shiftKey, ctrlKey, metaKey, altKey } = e
  198. delete this.keys[e.key]
  199. return {
  200. point: this.pointer?.point || [0, 0],
  201. origin: this.pointer?.origin || [0, 0],
  202. key: e.key,
  203. keys: Object.keys(this.keys),
  204. shiftKey,
  205. ctrlKey,
  206. metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
  207. altKey,
  208. }
  209. }
  210. pinch(point: number[], origin: number[]) {
  211. const { shiftKey, ctrlKey, metaKey, altKey } = this.keys
  212. const prev = this.pointer
  213. const delta = Vec.sub(origin, point)
  214. const info: TLPointerInfo<'pinch'> = {
  215. pointerId: 0,
  216. target: 'pinch',
  217. origin: prev?.origin || Vec.round(point),
  218. delta: delta,
  219. point: Vec.round(point),
  220. pressure: 0.5,
  221. shiftKey,
  222. ctrlKey,
  223. metaKey: Utils.isDarwin() ? metaKey : ctrlKey,
  224. altKey,
  225. }
  226. this.pointer = info
  227. return info
  228. }
  229. reset() {
  230. this.pointerUpTime = 0
  231. this.pointer = undefined
  232. this.keyboard = undefined
  233. this.keys = {}
  234. }
  235. static getPoint(
  236. e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent
  237. ): number[] {
  238. return [Number(e.clientX.toPrecision(5)), Number(e.clientY.toPrecision(5))]
  239. }
  240. static getPressure(e: PointerEvent | React.PointerEvent | Touch | React.Touch | WheelEvent) {
  241. return 'pressure' in e ? Number(e.pressure.toPrecision(5)) || 0.5 : 0.5
  242. }
  243. static commandKey(): string {
  244. return Utils.isDarwin() ? '⌘' : 'Ctrl'
  245. }
  246. }
  247. export const inputs = new Inputs()