Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animate leaving screen with React Navigation

I am developing a React Native application which uses React Navigation to manage the routing between screens.

I know reading React Navigation documentation and watching this video on Egghead.io (highly suggested) that is possible to define custom animations for transitions between screens passing the transitionConfig property to a StackNavigator.

For example, the following code is defining a slide from left animation that is basically the mirror of the iOS default push animation:

const TransitionConfig = () => ({
    screenInterpolator: ({ layout, position, scene }) => {
        const { index } = scene
        const { initWidth } = layout

        const translateX = position.interpolate({
            inputRange: [index - 1, index, index + 1],
            outputRange: [-initWidth, 0, 0],
        })

        return {
            transform: [{ translateX }],
        }
    }
})

const MyNavigator = createStackNavigator(
    {
        A: {screen: ScreenA},
        B: {screen: ScreenB},
    },
    { transitionConfig: TransitionConfig }
)

Considering the code above and the fact we are navigating from screen A to screen B, the screenInterpolator function body is basically describing the animation the screen B should follow when it's going to appear (it's reverted when it disappears). In this case the code it's saying to translate the screen B along the X axis to achieve a slide in effect.

Is it possible to also define the animation for the screen that is disappearing (screen A in our case)? I would like to define something like this:

  • B should appear sliding from left to right
  • A should disappear sliding from top to bottom
like image 767
toioski Avatar asked Nov 14 '18 16:11

toioski


1 Answers

I found the answer by myself, I hope will help someone in the future.

I was wrong assuming that screenInterpolator is describing only the behaviour of the screen B, because is actually describing the animation of both A and B. Or better, it's the interpolate function with inputRange and outputRange that allows you to describe the animation for both the entering (B) and leaving (A) screen.

Let's have a closer look to this snippet, keeping in mind that the interpolate function is just creating a 1-1 association between the values of inputRange and outputRange (deeper explanation here).

const { index } = scene

const translateX = position.interpolate({
    inputRange:  [index - 1, index, index + 1],
    outputRange: [-initWidth, 0, 0],
})

Assumptions:

  • B and A are screens
  • we are navigating from A to B
  • width(B) = width(A) = 320
  • width(deviceScreen) = 320
  • initWidth = 320

Explanation

  • index-1 represents the screen that is going to appear (screen B) and it's mapped to -initWidth. With this we are saying that the screen it's going to appear (screen B) should be X-translated (respect the viewport) of a value equal its width. So it will start the animation from X = 320 (i.e. outside the screen)
  • index represents the screen that is going to appear, but once is appeared (still screen B). Mapping it to 0 we are saying that screen B once is appeared should be in position X = 0
  • index + 1 represents the screen once is going to disappear. This is what it led me to the error. At the beginning I thought this was still and only the screen B, but just when it would have disappeared because of a navigation like B->C. However from our A->B point of view, it's the screen A the one that is going to disappear! So both line of thoughts are correct, it's all about seeing things from a different perspective. So, mapping index+1 to 0 we are saying that screen A should stay at X=0 when it's going to disappear (i.e. when B is going to appear)

Result

This is the animation realized by the code above. As you can see, the screen A remains where it is because of the association index+1 <-> 0 (no translation, so no animation for screen A).

Description here

If we change the association of index+1 to index+1 <-> -initWidth, then screen A will also be translated and hence animated :)

const translateX = position.interpolate({
  inputRange:  [index - 1, index, index + 1],
  outputRange: [initWidth, 0, -initWidth],
})

enter image description here


like image 126
toioski Avatar answered Oct 20 '22 00:10

toioski