react-native: [Navigator] Scene with transparent background doesn't show previous scene

The Modal docs say to use Navigator instead of Modal with a top-level Navigator. However, when setting backgroundColor: 'transparent' on the scene being pushed, the previous scene is not visible below. So how do we actually go about simulating a Modal with Navigator?

If there’s a way to do this already then I’d be willing to create a PR to update the docs to reflect that.

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Comments: 16 (3 by maintainers)

Most upvoted comments

I modified navigator’s code in react native and reached the goal.

You only need to take the following two steps:

  1. Config the next route’s sceneConfigs.

    {
        // No gestures.
        gestures: {},
    
        // Actually, I don't know this.
        springFriction: 26,
        // Decrease animation time, look like in a moment.
        springTension: 500,
    
        // Actually, I don't know this.
        defaultTransitionVelocity: 1,
    
        animationInterpolators: {
          // animation of this route's coming in
          into: buildStyleInterpolator({
              opacity: {
                value:1,
                type:'constant',
              },
          }),
          // animation of previous route's going out
          // This is the key: previous view no position change, no opacity change.
          out: buildStyleInterpolator({
              opacity: {
                value:1,
                type:'constant',
              },
          }),
        },
    },
    
  2. Modify navigator’s code, make the previous view keep static.

    If you do not do this, the previous view will move to the bottom of the screen.

    _disableScene: function(sceneIndex) {
      let sceneConstructor = this.refs['scene_' + sceneIndex];
      let nextRoute = this.state.routeStack[sceneIndex + 1];
    
      if (nextRoute && nextRoute.opts && nextRoute.opts.isPreViewStatic) {
          sceneConstructor.setNativeProps({
            pointerEvents: 'none',
          });
      } else {
          sceneConstructor.setNativeProps(SCENE_DISABLED_NATIVE_PROPS);
      }
    },
    

So, I made a Popup without Modal. Actually, this is a route, you can close this route in Android by pressing the back button.

What I want to know is how to config the duration of Navigator’s animation when push or pop a route?

@dragonwong Your solution is a great place to start. However I applied it a little bit differently:

_disableScene: function(sceneIndex) {
 let sceneConstructor = this._sceneRefs[sceneIndex];
 let nextRoute = this.state.routeStack[sceneIndex + 1];
 
 if (nextRoute && nextRoute.isPreViewStatic) {
    sceneConstructor.setNativeProps({
      pointerEvents: 'none',
    });
 } else {
    sceneConstructor.setNativeProps(SCENE_DISABLED_NATIVE_PROPS);
 }
},

This was so I just need to pass isPreViewStatic somewhere in the route object to trigger it.

One thing I noticed with this is when unmounting the component that I had this on, caused a re-render of the previous component causing the screen to flicker (more visible with transparent components)

In order to prevent that rerender I made the following change as well:

 _enableScene: function(sceneIndex) {
  // First, determine what the defined styles are for scenes in this navigator
  var sceneStyle = flattenStyle([styles.baseScene, this.props.sceneStyle]);
  // Then restore the pointer events and top value for this scene
  var enabledSceneNativeProps = {
    pointerEvents: 'auto',
    style: {
      top: sceneStyle.top,
      bottom: sceneStyle.bottom,
    },
   };
   if (sceneIndex !== this.state.transitionFromIndex &&
      sceneIndex !== this.state.presentedIndex) {
    // If we are not in a transition from this index, make sure opacity is 0
    // to prevent the enabled scene from flashing over the presented scene
    enabledSceneNativeProps.style.opacity = 0;
  }
  
 let sceneConstructor = this._sceneRefs[sceneIndex];
 let nextRoute = this.state.routeStack[sceneIndex + 1];
 
 if (nextRoute && nextRoute.isPreViewStatic) {
    sceneConstructor.setNativeProps({
      pointerEvents: 'auto',
    });
  } else {
    sceneConstructor.setNativeProps(enabledSceneNativeProps);
  }
},

Thanks @dragonwong @samzanemesis for the contribution

@dragonwong Hello, I have modified your solution a bit to be simpler if you just want to hard encapsulate a certain transition to not hide the parent component, this can be useful for more people

var SCENE_DISABLED_NATIVE_PROPS_VISIBLE = {
  pointerEvents: 'none',
};
  _disableScene: function(sceneIndex) {
    let sceneConstructor = this.refs['scene_' + sceneIndex];
    let nextRoute = this.state.routeStack[sceneIndex + 1];
    var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];

    if (sceneConfig == Navigator.SceneConfigs.FloatFromBottom ) {
        sceneConstructor.setNativeProps(SCENE_DISABLED_NATIVE_PROPS_VISIBLE);
    } else {
        sceneConstructor.setNativeProps(SCENE_DISABLED_NATIVE_PROPS);
    }
  },