Skip to content
This repository has been archived by the owner on Aug 8, 2024. It is now read-only.

Customizable per-screen transitions with StackNavigator #10

Closed
brentvatne opened this issue Feb 5, 2018 · 23 comments
Closed

Customizable per-screen transitions with StackNavigator #10

brentvatne opened this issue Feb 5, 2018 · 23 comments

Comments

@brentvatne
Copy link
Member

brentvatne commented Feb 5, 2018

This is highly requested and it would be very convenient. Need to outline the use cases, how some hypothetical new API would look and how it would improve over the current way to do this with separate stacks. Also, are there things we cannot currently do without screen-specific transitions?

react-navigation/react-navigation#2585 includes a link to several issues about this.

Copied over from @satya164's post on react-navigation/react-navigation#3217:


Recently I needed to customize screen transition animation for a single screen and it wasn't really straightforward. This proposal aims to provide a simple and straightforward way to specify screen transition for a single screen.

There is an existing proposal react-navigation/react-navigation#175, but it looks very confusing and difficult to use.

Problem

Currently, to be able to customize screen transition for a specific screen, you need to configure it globally and it's not very intuitive. It looks something like this:

navigationOptions: {
  transitionSpec: {
    duration: 200,
  },
  transitionConfig: () => ({
    screenInterpolator: props => {
      // Transitioning to search screen (navigate)
      if (props.scene.route.routeName === 'Search') {
        return CardStackStyleInterpolator.forFade(props);
      }

      const last = props.scenes[props.scenes.length - 1];

      // Transitioning from search screen (goBack)
      if (last.route.routeName === 'Search') {
        return CardStackStyleInterpolator.forFade(props);
      }

      return CardStackStyleInterpolator.forHorizontal(props);
    },
  }),
}

It's kinda weird and also doesn't allow me to specify a different duration for the back transition.

Proposal

When customizing the transition for a specific screen, we would want to customize 2 different transitions:

  1. Animation when navigating to the screen
  2. Animation when going back from the screen

For both animations, we should be able to specify which style properties should be animated, e.g. opacity, transform or both, also control the duration of both animations individually. We should also be able to specify transitions for the header buttons.

Keeping these in mind, the following would work for the above scenario:

type TransitionConfigurator = (
  toTransitionProps: TransitionProps,
  fromTransitionProps: TransitionProps) =>  {
    transitionSpec?: NavigationTransitionSpec,
    screenInterpolator?: Object,
    headerLeftInterpolator?: Object,
    headerTitleInterpolator?: Object,
    headerRightInterpolator?: Object,
  } 

This differs from the current type definition https://github.com/react-navigation/react-navigation/blob/master/src/TypeDefinition.js#L522 and therefore is a breaking change. However, it's possible to support both styles and deprecate the old style gradually without breaking existing code.

This also changes the behavior so that:

  1. Empty object (screenInterpolator: {}) will disable transition animations for any property (I think this is the current behavior too)
  2. null/undefined (screenInterpolator: null) will use the default transition animations (this is a new behavior)

Basically, this proposal aims to keep the semantics as close to the previous semantics but changes the way the options are specified to be more flexible.

Usage example

navigationOptions: {
  transitionConfig: (toTransitionProps, fromTransitionProps) => {
    const isBack = fromTransitionProps.navigation.state.index >= toTransitionProps.navigation.state.index;
    const routeName = isBack ? fromTransitionProps.scene.route.routeName : toTransitionProps.scene.route.routeName;

    // This check is only for the case where the transitionConfig is specified globally per navigator basis
    // If the config is specified per screen basis, then `routeName` will always refer to the current screen
    if (routeName === 'Search') {
      return {
        transitionSpec: { duration: isBack ? 150 : 200 },
        screenInterpolator: CardStackStyleInterpolator.forFade(props),
      }
    }
  },
}

Options specified per screen will always take priority over options specified globally.

When navigation from ScreenA -> ScreenB, the transitionConfig option on both screens are called with the same set of arguments, i.e. toTransitionProps.scene refers to ScreenB and fromTransitionProps refers to ScreenA. However, the config returned from ScreenA.navigationOptions.transitionConfig is applied only to ScreenA and the config returned from ScreenB.navigationOptions.transitionConfig is applied only to ScreenB.

@n1ru4l
Copy link

n1ru4l commented Mar 19, 2018

Would be awesome if there is also a way to specify the gestures for that screen. E.g. using the example of your Problem section results in a gesture that do not match the animation.

@DriesVS
Copy link

DriesVS commented Apr 3, 2018

I have the same fix! But as @n1ru4l pointed out, the gesture direction is defined by mode. Would be handy to dynamically configure the gesture direction based on sceneProps.

@jemmyphan
Copy link

@brentvatne if I'm not mistaken. since react-navigation/react-navigation@e27ad22, transitionConfig has had these parameters already. And it's working right now. is it safe to use it now? since the documentation hasn't been updated.

@brentvatne
Copy link
Member Author

@jemmyphan - I don't think so -- this works the same as before after that commit unless I'm missing something

@jemmyphan
Copy link

jemmyphan commented Apr 4, 2018

@brentvatne I totally missed react-navigation/react-navigation@2ee8548. 😅

@spruce-bruce
Copy link

This is related - have you thought much about allowing the ability to override transitions per navigate call as well? It's kind of edge casey, but I can imagine creating a screen and wanting to choose at time of navigation how the transition will happen.

props.navigation.navigate('Screen', params, transitionConfigOverride)?

@marcshilling
Copy link

What @spruce-bruce said above is what I need. My use case is a button that the user can tap to push a screen (with animation), versus me wanting to programmatically push that screen without animation when something else happens.

@n1ru4l
Copy link

n1ru4l commented May 2, 2018

@spruce-bruce Can't we have a more generic API that might be more expandable in the future?

e.g. like

props.navigation.navigate({
  screen: `Screen`,
  params: {},
  transitionConfig: {}
})

I prefer having a single object with sub-properties over having list of arguments

@spruce-bruce
Copy link

Certainly - I wouldn't be dogmatic about the api example I gave, just showing "fewest api changes" possible example to highlight the idea

@Bonobomagno
Copy link

Bonobomagno commented May 15, 2018

options specified per screen will always take priority over options specified globally.

Is this in react-navigation 2? Because it don't work on my navigator, using it per screen basis.

@djw27
Copy link

djw27 commented Jul 11, 2018

Just wanted to jump on board here and say that coming from ex-navigation this feels like an area that's lacking.

We use modals and stacks within tabs fairly interchangeably and made heavy use of specifying the following when using ex-navigation:

static route = {
  styles: {
    ...NavigationStyles.FloatVertical, 
 },
}

on screens which we wanted to appear as modals.

The initial proposal from @satya164 is fairly similar similar to this - is there anyone working on this which I can help out with or does this need starting afresh?

@satya164
Copy link
Member

@spruce-bruce @n1ru4l I think that's also possible with the proposed API. You'll need to pass some param which you can check in the transition config to return a different animation.

@fbn4sc
Copy link

fbn4sc commented Aug 9, 2018

I cannot find CardStackStyleInterpolator.

Any workaround?

@Sixbitunder
Copy link

Sixbitunder commented Aug 10, 2018

@fbn4sc the CardStackStyleInterpolator has been replaced by StackViewStyleInterpolator

you can Import it by adding

import StackViewStyleInterpolator from 'react-navigation/src/views/StackView/StackViewStyleInterpolator';

remeber also to switch props in sceneProps

@elizond0
Copy link

elizond0 commented Aug 17, 2018

@Sixbitunder
Thanks for your answer, it works.
when I swipe the page from left to right on android, how could navigation “go back” just like the ios gesture.

@kaantyy
Copy link

kaantyy commented Sep 19, 2018

I cannot find either CardStackStyleInterpolator or StackViewStyleInterpolator.

Workaround with v2.14.2?

@brentvatne
Copy link
Member Author

@nandorojo
Copy link

This would be an amazing addition.

@hxiongg
Copy link

hxiongg commented Mar 17, 2019

Any update on this?

@branegg-old
Copy link

:(

@brentvatne
Copy link
Member Author

stack is being rewritten - react-navigation/stack#131

@martinjuhasz
Copy link

Are there any news on this @brentvatne ?

@brentvatne
Copy link
Member Author

you can do this in v5. https://www.youtube.com/watch?v=PvjV96CNPqM. just use options on screen instead of on navigator

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests