Skip to content

Instantly share code, notes, and snippets.

@seflless
Created July 8, 2021 18:09
Show Gist options
  • Save seflless/810f907673df26fe551b996b1d053897 to your computer and use it in GitHub Desktop.
Save seflless/810f907673df26fe551b996b1d053897 to your computer and use it in GitHub Desktop.
useGesture Example
import appActor from '../state/actors/app';
import { useGesture } from '@use-gesture/react'
import { useEffect, useRef } from "react"
import { getCamera } from "../cameras/usePanZoom";
export function usePanZoomEvents() {
const panZoomStartPositionInWorldSpace = useRef()
console.log("usepanzoom");
useEffect(() => {
// As per the use-react-gesture document on pinching, we disable the safari gesture events
// https://use-gesture.netlify.app/docs/hooks/
document.addEventListener('gesturestart', e => e.preventDefault())
document.addEventListener('gesturechange', e => e.preventDefault())
const eventsOfInterest = [
"pointerdown",
"pointermove",
"pointerup"
];
const eventListeners = eventsOfInterest.map(eventName => {
const listener = (event) => {
appActor.send(event)
};
document.addEventListener(eventName, listener);
return listener;
})
return () => {
eventListeners.forEach((listener, index) => {
document.removeEventListener(eventsOfInterest[index], listener);
});
}
}, [])
useGesture(
{
onWheel: ({ event, delta }) => {
getCamera().setPosition(
getCamera().x + delta[0] * getCamera().z,
getCamera().y + delta[1] * getCamera().z,
getCamera().z
);
// This only gets called when a pure translation occurs, and happens from
// "mousewheel" gestures. On touchpads
appActor.send("Camera_Panned");
},
onPinch: ({ pinching, da, origin, offset, ...rest }) => {
// Notify app state machine a session of mix camera panning and/or zooming has stopped
if (!pinching) {
appActor.send("Camera_PanZoom_Stopped");
panZoomStartPositionInWorldSpace.current = undefined
return;
}
// Notify app state machine a session of mix camera panning and/or zooming has started
if (panZoomStartPositionInWorldSpace.current === undefined) {
appActor.send("Camera_PanZoom_Started");
// Remember the starting world position of the pinch gesture
panZoomStartPositionInWorldSpace.current = getCamera().screenToWorld({ x: origin[0], y: origin[1] });
}
// Calculate new zoom
// The 320 is a magic number that we can tweak more to get the exact zoom speed we want
// I wish it was more exact, as in where your fingers start in world coordinates are where
// they end in world coordinates
let deltaZ = -rest.delta[0] / 320.0 * getCamera().z;
if (getCamera().z + deltaZ < getCamera().minZoom) {
deltaZ = getCamera().minZoom - getCamera().z;
} else if (getCamera().z + deltaZ > getCamera().maxZoom) {
deltaZ = getCamera().maxZoom - getCamera().z;
}
const newZ = getCamera().z + deltaZ;
// Calculate new translation
const cameraSpace = {
x: -(origin[0] - window.innerWidth / 2.0) * newZ + panZoomStartPositionInWorldSpace.current.x,
y: -(origin[1] - window.innerHeight / 2.0) * newZ + panZoomStartPositionInWorldSpace.current.y
}
// Now set it, this will trigger all hooks that listen for camera position changes
getCamera().setPosition(cameraSpace.x, cameraSpace.y, newZ);
},
},
{
domTarget: document.querySelector(".tool-background"),
eventOptions: { passive: false }
}
)
}
// We nest this element which actually does all the event binding, so that hooks like
// useGesture don't trigger renders on every pan/zoom related event
function DocumentEventsInner() {
usePanZoomEvents();
return <></>
}
// Include this element to setup all the app events that are attached to document.body or window
function DocumentEvents() {
return <DocumentEventsInner />
}
export default DocumentEvents;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment