I am trying to build a draggable React-native Application with reanimated and gesture handler when i tried to implement a boundary for my draggable with reanimated clamp, after I move the draggables, the app crashes.
I tried implement my own function, still crashes the app.
Here's my code
const Dragable = props => {
const translateX = useSharedValue(props.x);
const translateY = useSharedValue(props.y);
const isGestureActive = useSharedValue(false);
const pan = Gesture.Pan()
.onStart(() => {
isGestureActive.value = true;
})
.onChange((evt) => {
translateX.value += clamp(evt.changeX,0,200);
translateY.value += clamp(evt.changeY,0,200);
})
.onEnd(() => {
isGestureActive.value = false;
})
const animatedStyle = useAnimatedStyle(() => {
const zIndex = isGestureActive.value ? 1000 : 1;
return {
zIndex,
transform: [
{ translateX: translateX.value },
{ translateY: translateY.value },
],
};
});
return (
<GestureDetector gesture={pan}>
<Animated.View style={animatedStyle}>
{some child components}
</Animated.View>
</GestureDetector>
);
};
Their clamp
hook is not available on my project neither, even though I'm using 3.3.0
. One solution is to use custom hooks. However, when react-native-reanimated is installed, the callbacks passed to the gestures are automatically workletized and run on the UI thread when called. Hence, using normal javaScript function
would cause a crash.
runOnJS(true)
.worklet
hooks, which is simply adding "worklet";
in the beginning of the hook and it's callbacks.Run Gesture callback on JS
const pan = Gesture.Pan()
.runOnJS(true)
.onStart(() => {
// some logic that can call JS hooks
})
.onChange(() => {
// some logic that can call JS hooks
})
.onEnd(() => {
// some logic that can call JS hooks
});
Run Gesture callback on UI (-= += clampOutRange2zero())
const clampOutRange2zero = (value, min = null, max = null) => {
"worklet";
const shouldClamp =
(typeof min === "number" && value < min) ||
(typeof max === "number" && value > max);
return shouldClamp ? 0 : value;
};
const pan = Gesture.Pan()
.onStart(() => {
isGestureActive.value = true;
})
.onChange((e) => {
translateX.value += clampOutRange2zero(e.changeX, 0, 200);
translateY.value += clampOutRange2zero(e.changeY, 0, 200);
})
.onEnd(() => {
isGestureActive.value = false;
});
Run Gesture callback on UI (= clamp())
const clamp = (value, min = null, max = null) => {
"worklet";
return typeof min === "number" && value < min
? min
: typeof max === "number" && value > max
? max
: value;
};
const pan = Gesture.Pan()
.onStart(() => {
isGestureActive.value = true;
})
.onChange((e) => {
translateX.value = clamp(translateX.value + e.changeX, 0, 200);
translateY.value = clamp(translateY.value + e.changeY, 0, 200);
})
.onEnd(() => {
isGestureActive.value = false;
});