Search

React Native Navigation, custom Scene (Screen) Transitions and interpolations

By Tim Rijavec | Code

This post is all about screen interpolation and transition settings using React Navigation provided from React Community inside your React Native project.

Before I start, I want to point out that there is official documentation you can find on this link about React Navigation but there is little to none info about custom transitions.

There were some questions popping out all over the community, from stackoverflow, their issue tracker, and on the react chat channel about how to create custom transitions, especially per screen transitions. 

This is not the only way you can declare custom transitions/interpolation but I think it's definitely the easiest and quite efficient way.

Let's dive directly in the code and prepare the navigator

Scene Components, Custom Transitions and Navigation Configurator 

Create a Scene/Screen or Two so you have something to navigate to and from

In the app you will usually split this into two seperate files SceneOne.js and SceneTwo.js. 

Please keep in mind that you can have everything in one file but I would rather encourage you to structure your files and have an awesomely organized app that you would be proud to open source if needed :)

class SceneOne extends Component {
    render() {
        return (
            <View>
                <Text>{'Scene One'}</Text>
            </View>
        )
    }
}
class SceneTwo extends Component {
    render() {
        return (
            <View>
                <Text>{'Scene Two'}</Text>
            </View>
        )
    }
}

Declare your app scenes

This is where you declare your scenes. Please don't be confused about the expression scene. I just prefer it over the screen. 

You can have as many scene configurations as you want.

let AppScenes = {
    SceneOne: {
        screen: SceneOne
    },
    SceneTwo: {
        screen: SceneTwo
    },
}

Declare custom transition

Let's declare our first custom transition as a function that accepts two parameters, index and position. 

All indexes start with 0. When you navigate from one scene to another, index value goes up by 1, and when you navigate back, or pop the scene, the index value goes down by 1.

For example if we check previously declared scenes and take SceneOne as the main/root scene with index 0. When you navigate to SceneTwo, the index passed through to the transition will be 1 and when you further navigate to another scene (We don't have one in this example), then the index will be 2. On the other side, if you navigate back to SceneOne, index will be 0 again.

With this in mind, we can actually declare transitions from SceneOne to SceneTwo, transitions from SceneTwo back to SceneOne and even transition of SceneTwo to another scene. So go ahead and declare the input range variable inputRange with those three indices.

Next thing is the output range of the interpolation. We can use the provided position variable, which reflex input range indiced and apply interpolations to get the desired effects. Because of the transitioner using native animations, we are somewhat restricted by some parameters. For example you cannot change height or width values of the scene. Please see the the screen shot for the error you'll get if you'll try to animate width/height.

let MyTransition = (index, position) => {
    const inputRange = [index - 1, index, index + 1];
    const outputRange = [.8, 1, 1];
    const opacity = position.interpolate({
        inputRange,
        outputRange,
    });

    const scaleY = position.interpolate({
        inputRange,
        outputRange,
    });

    return {
    opacity,
        transform: [
            {scaleY}
        ]
    };
};

 

Declare custom transitions configurator

The configurator used by the StackNavigator. Configurator is a function that returns an object that overrides default screen transitions. 

In the object you can define screenInterpolation and one additional paramter for tweaking details about the speed, duration, etc.

screenInterpolation 
is called every time, before the transition. I could be wrong with this but this interpolator is actully called for every scene on the stack, everytime, just before the transition. 

If you recall from the previous step we needed two variables for our transition (index and position). We can access both of them from the scene props passed through to the interpolator. 

let TransitionConfiguration = () => {
    return {
        // Define scene interpolation, eq. custom transition
        screenInterpolator: (sceneProps) => {
            const {position, scene} = sceneProps;
            const {index} = scene;

            return MyTransition(index, position);
        }
    }
};

Create app navigator using Stack Navigator

This is a standard way of setting up the stack navigator. I'm not dive into details about it, and you can read more about it on this link.

The only think I will mention is that as second parameter to the function, you can pass through an object, where you override the default transition config with your custom one.

const AppNavigator = StackNavigator(AppScenes, {
    transitionConfig: TransitionConfiguration
});

Use App Navigator in your project

Using the navigatior on the entry component of your app.

class App extends Component {
    return (
    <View>
        <AppNavigator />
    </View>
    )
}

Register your app in eq. `index.ios.js`

Registring your app to react native.

import { AppRegistry } from 'react-native';
AppRegistry.registerComponent('MyApp', () => App);

Examples

1. How to actually set custom transition per scene?

When you navigate using NavigationActions from React Navigation, you can pass through some additional parametes. In the example below, I'm setting transition parameter, which is a name of our custom transition. It can be anything you want and for that, please see the next step.

Note that I'm calling action navigate from props in this case, that is being mapped to the dispatch function on component using navigate: NavigationActions.navigate 

this.props.navigate({
    routeName: 'SceneTwo',
    params: {
        transition: 'myCustomTransition'
    }
})

There are also some small changes we need to do inside the transition configurator.

Let's first create another custom transition first and name it MyCustomTransition. It is a little bit more complex one than our first custom transition. You can read more about the interpolation on this link.

export const MyCustomTransition = (index, position) => {
    const inputRange = [index - 1, index, index + 0.99, index + 1];

    const opacity = position.interpolate({
        inputRange,
        outputRange: ([0, 1, 1, 0]),
    });

    const translateX = 0;
    const translateY = position.interpolate({
        inputRange,
        outputRange: ([50, 0, 0, 0]),
    });

    return {
        opacity,
        transform: [
            { translateX },
            { translateY }
        ],
    };
};

We can access the transition parameter that we set in the navigate action inside route.params variable. route variable on the other hand is set inside the scene alongside with the index.

Now that we have our second transition ready, we can easily switch between the two using a simple switch shorthand. Please see the code below.

let TransitionConfiguration = () => {
    return {
        // Define scene interpolation, eq. custom transition
        screenInterpolator: (sceneProps) => {

            const {position, scene} = sceneProps;
            const {index, route} = scene;
            const params = route.params || {};
            const transition = params.transition || 'default';  
return { myCustomTransition: MyCustomTransition(index, position), default: MyTransition(index, position), }[transition]; } } };

2. How to tweak speed and other settings?

There is really not much to say about this one. As I mentioned before, you can define additional parameter inside the transition configurator. This paramere is called transitionSpec.

Let's define the specs to make our transitions super slow, and also apply some normal bezier easing. Definition below will 

const MyTransitionSpec = ({
    duration: 2000,
    easing: Easing.bezier(0.2833, 0.99, 0.31833, 0.99),
    timing: Animated.timing,
});

Now as for the second part, we actually need to change the configuator a little bit.

We need to override transitionSpec inside the configurator using our custom one.

export default TransitionConfiguration = () => {
    return {
        transitionSpec: MyTransitionSpec,
        screenInterpolator: (sceneProps) => {
           ...
        }
    }
};
Post a comment
listings__registration-popup__header-smaller Get our free insider house sitting e-guide highlights - and free access to search house sits

As Featured In...

Awards