Search code examples
react-nativereact-native-reanimated

convert reanimated v1 math to v3


I want to use Reanimated v3 to using Shared Element Transition function, but I got many error after upgrade, eg:

const animatedTranslateX = multiply(
    cond(
      greaterThan(x, threshold),
      isRTL ? SCREEN_WIDTH - stickyItemWidth : itemWidth - stickyItemWidth,
      isRTL
        ? add(x, SCREEN_WIDTH - itemWidth - separatorSize)
        : sub(x, separatorSize)
    ),
    isRTL ? 1 : -1
  );

multiply, cond, greaterThan, add, sub has deprecated and fully removed from v3, I tried to convert to v3 but not working, can someone give me some advices or converting tutorial? THanks so much


Solution

  • so reanimated v3 makes this really easy. Instead of having to use helper functions for maths you will just be able to use regular js.

    Let's look at what you have here. So x is driving the animation here.

    const animatedTranslateX = multiply(
        cond(
          greaterThan(x, threshold),
          isRTL ? SCREEN_WIDTH - stickyItemWidth : itemWidth - stickyItemWidth,
          isRTL
            ? add(x, SCREEN_WIDTH - itemWidth - separatorSize)
            : sub(x, separatorSize)
        ),
        isRTL ? 1 : -1
      );
    

    To migrate this code, the first step would be to set up a shared value.

    const x = useSharedValue(0)
    

    Then you need something to change this shared value. E.g. setup the scrollview to update this value. The docs will explain in with an example, but the gist would be to use the following, if you want it all to happen on the ui thread. All you need to keep in mind is to use the x.value =... when you want to update the shared value. This is new from the old version, where the value of x was not as easily accessible. You would have needed to call set(x, 100) or something like that.

    const scrollHandler = useAnimatedScrollHandler(
        {
            onScroll: event => x.value = event.contentOffset.x
        }
    );
    
    <Animated.ScrollView
        onScroll={scrollHandler}
        scrollEventThrottle={16} // don't forget this
    >
        {children}
    </Animated.ScrollView>
    

    Then create a derrived value:

    const animatedTranslateX = useDerivedValue(() => {
    
       let result = 0;
    
       // instead of cond we use regular if
       if(x.value > threshold) {
          result = isRTL ? SCREEN_WIDTH - stickyItemWidth : itemWidth - stickyItemWidth,
       } else {
          // instead of add and sub we just use + and -
          result = isRTL 
              ? x.value + SCREEN_WIDTH - itemWidth - separatorSize
              : x.value - separatorSize
       }
    
       // instead of mul we just *
       result = result * isRTL ? 1 : -1
    
       return result;
    })
    

    This was a bit clunky, but I wanted to the literal translation first. We could clean up the code of course, here you can do any old js. E.g. already multiply isRTL. All you need to keep in mind is that you have to "dereference" the shared values by using the x.value.

    const animatedTranslateX = useDerivedValue(() => {
       if(x.value > threshold) return isRTL ? -SCREEN_W + stickyW : itemW - stickyW
       else return isRTL ? -x.value - SCREEN_W + itemW + sepSize : x.value - sepSize
    })
    

    After this you can create a animated style using this translation:

    const animatedStyle = useAnimatedStyle(() => {
       return {
           transform: [
             {translateX: animatedTranslateX.value} // here we also need to dereference the derived animated value
           ]
       }
    })
    
    and apply it as usual:
    <Animated.View style={animatedStyle} />
    

    You can also do the calculation directly in the useAnimatedStyle callback and skip the derived value creation, which most people will do. As soon as you figured out how to create a shared value and drive it using a gesture or scroll view you will be able to use it within the useAnimatedStyle and do just regular math instead of using the old callback functions.

    The old way was configuring the math function you wanted to to run on native side, e.g. using add(). RN3 has a own JS thread so they use a babel plugin to extract the js code you've written (within the useAnimatedStyle hook, lookup "worklets" if you want to know more) and run that on the JS thread. So you can do regular js things within the hook (except call other functions... which are not marked as worklets that is).

    Best of luck.