Reanimated clamp crashes on panguesture

63 Views Asked by At

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>
    );
};
1

There are 1 best solutions below

0
Iman Maleki On BEST ANSWER

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.

  • One way is to use runOnJS(true).
  • Another way is to use 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;
    });