react-native-gesture-handler
Version:
Declarative API exposing native platform touch and gesture system to React Native
120 lines (99 loc) • 3 kB
text/typescript
import { Reanimated } from '../../../handlers/gestures/reanimatedWrapper';
import { NativeProxy } from '../../NativeProxy';
import type {
BaseGestureConfig,
SharedValue,
SharedValueOrT,
} from '../../types';
import { HandlerCallbacks } from './propsWhiteList';
// Variant of djb2 hash function.
// Taken from https://gist.github.com/eplawless/52813b1d8ad9af510d85?permalink_comment_id=3367765#gistcomment-3367765
function hash(str: string) {
'worklet';
const len = str.length;
let h = 5381;
for (let i = 0; i < len; i++) {
h = (h * 33) ^ str.charCodeAt(i);
}
return h >>> 0;
}
export const SHARED_VALUE_OFFSET = 1.618;
// Don't transfer entire NativeProxy to the UI thread
const { updateGestureHandlerConfig } = NativeProxy;
export function bindSharedValues<
TConfig,
THandlerData,
TExtendedHandlerData extends THandlerData,
>(
config: BaseGestureConfig<TConfig, THandlerData, TExtendedHandlerData>,
handlerTag: number
) {
if (Reanimated === undefined) {
return;
}
const baseListenerId = handlerTag + SHARED_VALUE_OFFSET;
const { shouldUseReanimatedDetector } = config;
const attachListener = (sharedValue: SharedValue, configKey: string) => {
'worklet';
const keyHash = hash(configKey);
const listenerId = baseListenerId + keyHash;
sharedValue.addListener(listenerId, (value) => {
updateGestureHandlerConfig(
handlerTag,
configKey === 'runOnJS'
? {
dispatchesReanimatedEvents: shouldUseReanimatedDetector && !value,
}
: { [configKey]: value }
);
});
};
for (const [key, maybeSharedValue] of Object.entries(config)) {
if (!Reanimated.isSharedValue(maybeSharedValue)) {
continue;
}
Reanimated.runOnUI(attachListener)(maybeSharedValue, key);
}
}
export function unbindSharedValues<
TConfig,
THandlerData,
TExtendedHandlerData extends THandlerData,
>(
config: BaseGestureConfig<TConfig, THandlerData, TExtendedHandlerData>,
handlerTag: number
) {
if (Reanimated === undefined) {
return;
}
const baseListenerId = handlerTag + SHARED_VALUE_OFFSET;
for (const [key, maybeSharedValue] of Object.entries(config)) {
if (!Reanimated.isSharedValue(maybeSharedValue)) {
continue;
}
const keyHash = hash(key);
const listenerId = baseListenerId + keyHash;
Reanimated.runOnUI(() => {
maybeSharedValue.removeListener(listenerId);
})();
}
}
export function hasWorkletEventHandlers<
TConfig,
THandlerData,
TExtendedHandlerData extends THandlerData,
>(config: BaseGestureConfig<TConfig, THandlerData, TExtendedHandlerData>) {
for (const key of HandlerCallbacks) {
const value = config[key];
if (typeof value === 'function' && '__workletHash' in value) {
return true;
}
}
return false;
}
export function maybeUnpackValue<T>(
v: SharedValueOrT<T, boolean> | undefined
): T {
'worklet';
return (Reanimated?.isSharedValue(v) ? v.value : v) as T;
}