Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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