useGestureEvents.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import Vec from '@tldraw/vec'
  2. import type { Handler, WebKitGestureEvent } from '@use-gesture/core/types'
  3. import { useGesture } from '@use-gesture/react'
  4. import * as React from 'react'
  5. import { useRendererContext } from '~hooks'
  6. import { TLTargetType, TLViewport } from '@tldraw/core'
  7. type PinchHandler = Handler<
  8. 'pinch',
  9. WheelEvent | PointerEvent | TouchEvent | WebKitGestureEvent | KeyboardEvent
  10. >
  11. export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
  12. const { viewport, inputs, callbacks } = useRendererContext()
  13. const events = React.useMemo(() => {
  14. const onWheel: Handler<'wheel', WheelEvent> = gesture => {
  15. const { event, delta } = gesture
  16. event.preventDefault()
  17. if (inputs.state === 'pinching') return
  18. if (Vec.isEqual(delta, [0, 0])) return
  19. callbacks.onWheel?.(
  20. {
  21. type: TLTargetType.Canvas,
  22. order: 0,
  23. delta: gesture.delta,
  24. point: inputs.currentPoint,
  25. },
  26. event
  27. )
  28. }
  29. const onPinchStart: PinchHandler = gesture => {
  30. const elm = ref.current
  31. const { event } = gesture
  32. if (!(event.target === elm || elm?.contains(event.target as Node))) return
  33. if (!['idle', 'panning'].includes(inputs.state)) return
  34. callbacks.onPinchStart?.(
  35. {
  36. type: TLTargetType.Canvas,
  37. order: 0,
  38. delta: gesture.delta,
  39. offset: gesture.offset,
  40. point: Vec.sub(gesture.origin, inputs.containerOffset),
  41. },
  42. event
  43. )
  44. }
  45. const onPinch: PinchHandler = gesture => {
  46. const elm = ref.current
  47. const { event } = gesture
  48. if (!(event.target === elm || elm?.contains(event.target as Node))) return
  49. if (inputs.state !== 'pinching') return
  50. callbacks.onPinch?.(
  51. {
  52. type: TLTargetType.Canvas,
  53. order: 0,
  54. delta: gesture.delta,
  55. offset: gesture.offset,
  56. point: Vec.sub(gesture.origin, inputs.containerOffset),
  57. },
  58. event
  59. )
  60. }
  61. const onPinchEnd: PinchHandler = gesture => {
  62. const elm = ref.current
  63. const { event } = gesture
  64. if (!(event.target === elm || elm?.contains(event.target as Node))) return
  65. if (inputs.state !== 'pinching') return
  66. callbacks.onPinchEnd?.(
  67. {
  68. type: TLTargetType.Canvas,
  69. order: 0,
  70. delta: gesture.delta,
  71. offset: gesture.offset,
  72. point: Vec.sub(gesture.origin, inputs.containerOffset),
  73. },
  74. event
  75. )
  76. }
  77. return {
  78. onWheel,
  79. onPinchStart,
  80. onPinchEnd,
  81. onPinch,
  82. }
  83. }, [callbacks])
  84. useGesture(events, {
  85. target: ref,
  86. eventOptions: { passive: false },
  87. pinch: {
  88. from: [viewport.camera.zoom, viewport.camera.zoom],
  89. scaleBounds: () => ({
  90. from: viewport.camera.zoom,
  91. max: TLViewport.maxZoom,
  92. min: TLViewport.minZoom,
  93. }),
  94. },
  95. })
  96. }