tldraw/hooks/useZoomEvents.ts

102 lines
2.5 KiB
TypeScript
Raw Normal View History

2021-05-09 13:04:42 +00:00
import React, { useEffect, useRef } from "react"
import state from "state"
2021-05-13 08:34:56 +00:00
import inputs from "state/inputs"
2021-05-09 13:04:42 +00:00
import * as vec from "utils/vec"
2021-05-28 13:08:51 +00:00
import { usePinch } from "react-use-gesture"
2021-05-09 13:04:42 +00:00
2021-05-09 21:22:25 +00:00
/**
* Capture zoom gestures (pinches, wheels and pans) and send to the state.
* @param ref
* @returns
*/
2021-05-09 13:04:42 +00:00
export default function useZoomEvents(
ref: React.MutableRefObject<SVGSVGElement>
) {
const rTouchDist = useRef(0)
useEffect(() => {
const element = ref.current
if (!element) return
function handleWheel(e: WheelEvent) {
e.preventDefault()
if (e.ctrlKey) {
state.send("ZOOMED_CAMERA", {
delta: e.deltaY,
2021-05-13 08:34:56 +00:00
...inputs.wheel(e),
2021-05-09 13:04:42 +00:00
})
return
}
state.send("PANNED_CAMERA", {
delta: [e.deltaX, e.deltaY],
2021-05-13 08:34:56 +00:00
...inputs.wheel(e),
2021-05-09 13:04:42 +00:00
})
}
function handleTouchMove(e: TouchEvent) {
2021-05-09 21:22:25 +00:00
e.preventDefault()
2021-05-09 13:04:42 +00:00
if (e.touches.length === 2) {
const { clientX: x0, clientY: y0 } = e.touches[0]
const { clientX: x1, clientY: y1 } = e.touches[1]
const dist = vec.dist([x0, y0], [x1, y1])
2021-05-09 21:22:25 +00:00
const point = vec.med([x0, y0], [x1, y1])
2021-05-09 13:04:42 +00:00
2021-05-09 21:22:25 +00:00
state.send("WHEELED", {
delta: dist - rTouchDist.current,
point,
})
2021-05-09 13:04:42 +00:00
rTouchDist.current = dist
}
}
element.addEventListener("wheel", handleWheel)
element.addEventListener("touchstart", handleTouchMove)
element.addEventListener("touchmove", handleTouchMove)
return () => {
element.removeEventListener("wheel", handleWheel)
element.removeEventListener("touchstart", handleTouchMove)
element.removeEventListener("touchmove", handleTouchMove)
}
}, [ref])
2021-05-28 13:08:51 +00:00
const rPinchDa = useRef<number[] | undefined>(undefined)
const rPinchAngle = useRef<number>(undefined)
const rPinchPoint = useRef<number[] | undefined>(undefined)
const bind = usePinch(({ pinching, da, origin }) => {
if (!pinching) {
state.send("STOPPED_PINCHING")
rPinchDa.current = undefined
rPinchPoint.current = undefined
return
}
if (rPinchPoint.current === undefined) {
state.send("STARTED_PINCHING")
rPinchDa.current = da
rPinchPoint.current = origin
}
const [distanceDelta, angleDelta] = vec.sub(rPinchDa.current, da)
state.send("PINCHED", {
delta: vec.sub(rPinchPoint.current, origin),
point: origin,
distanceDelta,
angleDelta,
})
rPinchDa.current = da
rPinchPoint.current = origin
})
return { ...bind() }
2021-05-09 13:04:42 +00:00
}