Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

useZoomEvents.ts 2.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import React, { useEffect, useRef } from "react"
  2. import state from "state"
  3. import inputs from "state/inputs"
  4. import * as vec from "utils/vec"
  5. import { usePinch } from "react-use-gesture"
  6. /**
  7. * Capture zoom gestures (pinches, wheels and pans) and send to the state.
  8. * @param ref
  9. * @returns
  10. */
  11. export default function useZoomEvents(
  12. ref: React.MutableRefObject<SVGSVGElement>
  13. ) {
  14. const rTouchDist = useRef(0)
  15. useEffect(() => {
  16. const element = ref.current
  17. if (!element) return
  18. function handleWheel(e: WheelEvent) {
  19. e.preventDefault()
  20. if (e.ctrlKey) {
  21. state.send("ZOOMED_CAMERA", {
  22. delta: e.deltaY,
  23. ...inputs.wheel(e),
  24. })
  25. return
  26. }
  27. state.send("PANNED_CAMERA", {
  28. delta: [e.deltaX, e.deltaY],
  29. ...inputs.wheel(e),
  30. })
  31. }
  32. function handleTouchMove(e: TouchEvent) {
  33. e.preventDefault()
  34. if (e.touches.length === 2) {
  35. const { clientX: x0, clientY: y0 } = e.touches[0]
  36. const { clientX: x1, clientY: y1 } = e.touches[1]
  37. const dist = vec.dist([x0, y0], [x1, y1])
  38. const point = vec.med([x0, y0], [x1, y1])
  39. state.send("WHEELED", {
  40. delta: dist - rTouchDist.current,
  41. point,
  42. })
  43. rTouchDist.current = dist
  44. }
  45. }
  46. element.addEventListener("wheel", handleWheel)
  47. element.addEventListener("touchstart", handleTouchMove)
  48. element.addEventListener("touchmove", handleTouchMove)
  49. return () => {
  50. element.removeEventListener("wheel", handleWheel)
  51. element.removeEventListener("touchstart", handleTouchMove)
  52. element.removeEventListener("touchmove", handleTouchMove)
  53. }
  54. }, [ref])
  55. const rPinchDa = useRef<number[] | undefined>(undefined)
  56. const rPinchAngle = useRef<number>(undefined)
  57. const rPinchPoint = useRef<number[] | undefined>(undefined)
  58. const bind = usePinch(({ pinching, da, origin }) => {
  59. if (!pinching) {
  60. state.send("STOPPED_PINCHING")
  61. rPinchDa.current = undefined
  62. rPinchPoint.current = undefined
  63. return
  64. }
  65. if (rPinchPoint.current === undefined) {
  66. state.send("STARTED_PINCHING")
  67. rPinchDa.current = da
  68. rPinchPoint.current = origin
  69. }
  70. const [distanceDelta, angleDelta] = vec.sub(rPinchDa.current, da)
  71. state.send("PINCHED", {
  72. delta: vec.sub(rPinchPoint.current, origin),
  73. point: origin,
  74. distanceDelta,
  75. angleDelta,
  76. })
  77. rPinchDa.current = da
  78. rPinchPoint.current = origin
  79. })
  80. return { ...bind() }
  81. }