@shopify/react-native-skia
Version:
High-performance React Native Graphics using Skia
108 lines (95 loc) • 3.63 kB
text/typescript
import type { DependencyList } from "react";
import { useCallback, useRef } from "react";
import type { Vector } from "../skia/types";
import { Platform } from "../Platform";
import type {
ExtendedTouchInfo,
TouchHandlers,
TouchHandler,
TouchInfo,
} from "./types";
import { TouchType } from "./types";
const useInternalTouchHandler = (
handlers: TouchHandlers,
deps: DependencyList = [],
multiTouch = false
): TouchHandler => {
const prevTouchInfoRef = useRef<{ [key: number]: TouchInfo | undefined }>({});
const prevVelocityRef = useRef<{ [key: number]: Vector | undefined }>({});
return useCallback((history: Array<Array<TouchInfo>>) => {
// Process all items in the current touch history
history.forEach((touches) => {
// Enumerate touches
for (let i = 0; i < touches.length; i++) {
if (!multiTouch && i > 0) {
break;
}
const touch = touches[i];
const prevTouch = prevTouchInfoRef.current[touch.id];
// Calculate the velocity from the previous touch.
const timeDiffseconds =
touch.timestamp -
(prevTouchInfoRef.current[touch.id]?.timestamp ?? touch.timestamp);
const distX = touch.x - (prevTouch?.x ?? touch.x);
const distY = touch.y - (prevTouch?.y ?? touch.y);
if (
touch.type !== TouchType.Start &&
touch.type !== TouchType.End &&
touch.type !== TouchType.Cancelled &&
timeDiffseconds > 0
) {
prevVelocityRef.current[touch.id] = {
x: distX / timeDiffseconds / Platform.PixelRatio,
y: distY / timeDiffseconds / Platform.PixelRatio,
};
}
const extendedTouchInfo: ExtendedTouchInfo = {
...touch,
velocityX: prevVelocityRef.current[touch.id]?.x ?? 0,
velocityY: prevVelocityRef.current[touch.id]?.y ?? 0,
};
// Save previous touch
prevTouchInfoRef.current[touch.id] = touch;
if (touch.type === TouchType.Start) {
delete prevVelocityRef.current[touch.id];
handlers.onStart && handlers.onStart(touch);
} else if (touch.type === TouchType.Active) {
handlers.onActive && handlers.onActive(extendedTouchInfo);
} else {
handlers.onEnd && handlers.onEnd(extendedTouchInfo);
}
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
};
/**
* Provides a callback for handling touch events in the Skia View.
* This touch handler only handles single touches.
* @param handlers Callbacks for the different touch states
* @param deps optional Dependency array to update the handlers
* @returns A function that can be used from within the onDraw callback to
* update and handle touch events. Call it with the touches property from
* the info object.
*/
export const useTouchHandler = (
handlers: TouchHandlers,
deps: DependencyList = []
): TouchHandler => {
return useInternalTouchHandler(handlers, deps, false);
};
/**
* Provides a callback for handling touch events in the Skia View.
* This touch handler handles multiple touches.
* @param handlers Callbacks for the different touch states
* @param deps optional Dependency array to update the handlers
* @returns A function that can be used from within the onDraw callback to
* update and handle touch events. Call it with the touches property from
* the info object.
*/
export const useMultiTouchHandler = (
handlers: TouchHandlers,
deps: DependencyList = []
): TouchHandler => {
return useInternalTouchHandler(handlers, deps, true);
};