Home  >  Q&A  >  body text

React Native useAnimatedGestureHandler is not called only in onStart on web

<p><strong>Before I realized onStart() was not called</strong></p> <p>Using PanGestureHandler on the web and trying to "drag" an AnimatedView does not work on the web. There are no obvious errors, the application builds fine and there are no warnings in the console when checking. </p> <p>There is a warning that leads me to believe this may be the source of the problem. I get a warning on the console that reads: </p> <p><code>"transform" style array values ​​are deprecated. Please use a space-delimited string function, such as "scaleX(2) rotateX(15deg)". </code></p> <p>I'm using an AnimatedView with containerStyle to transform and move objects when dragged. </p> <p><strong>The root of the problem</strong></p> <p>So I looked into the problem further, trying to debug it, and I realized that the onStart() callback was not being called. Since the onStart() callback is not called, the context value is never set and the context object remains empty overall. This led to my original problem, being unable to drag objects. </p> <p>However, it will still work on iOS. For some reason on iOS the onStart() callback is called. This results in the context being populated and working fine. </p> <p>This is my code, remember this is just a component. In the root directory, I do have a GestureHandlerRootView component wrapping the entire application.</p> <pre class="brush:php;toolbar:false;">import { View, Image } from 'react-native'; import Animated, { useAnimatedStyle, useSharedValue, useAnimatedGestureHandler, withSpring, } from 'react-native-reanimated'; import { PanGestureHandler, TapGestureHandler } from 'react-native-gesture-handler'; const AnimatedImage = Animated.createAnimatedComponent(Image); const AnimatedView = Animated.createAnimatedComponent(View); export default function EmojiSticker ({ imageSize, stickerSource }) { const scaleImage = useSharedValue(imageSize); const translateX = useSharedValue(0); const translateY = useSharedValue(0); const onDoubleTap = useAnimatedGestureHandler({ onActive: () => { if (scaleImage.value !== imageSize * 2) { scaleImage.value = scaleImage.value * 2; } else { scaleImage.value = scaleImage.value / 2; } }, }); const onDrag = useAnimatedGestureHandler({ onStart: (event, context) => { context.translateX = translateX.value; context.translateY = translateY.value; }, onActive: (event, context) => { translateX.value = event.translationX context.translateX; translateY.value = event.translationY context.translateY; }, }); const imageStyle = useAnimatedStyle(() => { return { width: withSpring(scaleImage.value), height: withSpring(scaleImage.value), }; }); const containerStyle = useAnimatedStyle(() => { return { transform: [ { translateX: translateX.value, }, { translateY: translateY.value, }, ], }; }); return ( <PanGestureHandler onGestureEvent={onDrag}> <AnimatedView style={[containerStyle, { top: -350 }]}> <TapGestureHandler onGestureEvent={onDoubleTap} numberOfTaps={2}> <AnimatedImage source={stickerSource} resizeMode='contain' style={[imageStyle, { width: imageSize, height: imageSize }]} /> </TapGestureHandler> </AnimatedView> </PanGestureHandler> ); }</pre> <p><strong>顺便说一下,双击手势在 Web 和 iOS 上都完美工作。</strong> 我感到困惑,因为拖动在 iOS 上完美工作,但在 Web 上却不行。The deprecation of the transform style has me trying to figure out a way to create web-specific styles, but I'm having trouble finding a situation where others have encountered this problem. I believe there is a real solution, I just may be missing it. I'm just really confused because it works perfectly on iOS but not on the web. </p> <p>I tried to see if anyone else had a similar problem but didn't actually find anything relevant. I also tried searching on the console for the warning I saw. </p> <p><code>"transform" style array values ​​are deprecated. Please use a space-delimited string function, such as "scaleX(2) rotateX(15deg)". </code></p> <p>At least when I searched for something related to React-Native, I didn't find anything related. </p> <p>I would love to find a draggable solution on the web. </p>
P粉667649253P粉667649253452 days ago506

reply all(1)I'll reply

  • P粉293550575

    P粉2935505752023-08-17 11:02:56

    I solved this problem by consulting the documentation of react-native-reanimated. Apparently useAnimatedGestureHandler is not deprecated as it works in onDoubleTap, not to mention onDrag also works fine on iOS.

    But in the documentation dealing with pan gestures I found this:

    const pan = Gesture.Pan()
      .onBegin(() => {
        pressed.value = true;
      })
      .onChange((event) => {
        offset.value = event.translationX;
      })
      .onFinalize(() => {
        offset.value = withSpring(0);
        pressed.value = false;
      });

    So, there is no need to import PanGestureHandler and TapGestureHandler from 'react-native-gesture-handler', nor from 'react-native-reanimated' useAnimatedGestureHandler, just import Gesture and GestureDetector from 'react-native-gesture-handler'.

    Gesture replaces useAnimatedGestureHandler, and GestureDetector replaces components such as PanGestureHandler and TapGestureHandler.

    I also need to create my own contextX and contextY variables using useSharedValue() because as far as I know, onBegin()andonChange()The callback function has no context to set.

    Anyway, here is the fixed code, which now runs perfectly on both the web and iOS:

    import { View, Image } from 'react-native';
    import Animated, {
      useAnimatedStyle,
      useSharedValue,
      withSpring,
    } from 'react-native-reanimated';
    import { Gesture, GestureDetector } from 'react-native-gesture-handler';
    
    const AnimatedImage = Animated.createAnimatedComponent(Image);
    const AnimatedView = Animated.createAnimatedComponent(View);
    
    export default function EmojiSticker({ imageSize, stickerSource }) {
      const scaleImage = useSharedValue(imageSize);
      const translateX = useSharedValue(0);
      const translateY = useSharedValue(0);
      const contextX = useSharedValue(0);
      const contextY = useSharedValue(0);
    
      const onDoubleTap = Gesture.Tap().numberOfTaps(2)
        .onEnd(() => {
          if (scaleImage.value !== imageSize * 2) {
            scaleImage.value = scaleImage.value * 2;
          } else {
            scaleImage.value = scaleImage.value / 2;
          }
        });
      const onDrag = Gesture.Pan()
        .onBegin(() => {
          contextX.value = translateX.value;
          contextY.value = translateY.value;
        })
        .onChange((event) => {
          translateX.value = event.translationX + contextX.value;
          translateY.value = event.translationY + contextY.value;
        });
    
      const imageStyle = useAnimatedStyle(() => {
        return {
          width: withSpring(scaleImage.value),
          height: withSpring(scaleImage.value),
        };
      });
      const containerStyle = useAnimatedStyle(() => {
        return {
          transform: [
            {
              translateX: translateX.value,
            },
            {
              translateY: translateY.value,
            },
          ],
        };
      });
    
      return (
        <GestureDetector gesture={onDrag}>
          <AnimatedView style={[containerStyle, { top: -350 }]}>
            <GestureDetector gesture={onDoubleTap}>
              <AnimatedImage
                source={stickerSource}
                resizeMode="contain"
                style={[imageStyle, { width: imageSize, height: imageSize }]}
              />
            </GestureDetector>
          </AnimatedView>
        </GestureDetector>
      );
    }

    reply
    0
  • Cancelreply