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

Created on 2 Dec 2015  路  16Comments  路  Source: facebook/react-native

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.

JavaScript Locked

Most helpful comment

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.

``` js
{
// 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',
         },
     }),
   },

},
```

  1. 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.

``` js
_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?

All 16 comments

Closing to ask on StackOverflow first.

@fender did you ever solve this issue? I'm having the same problem. Thanks

@fender Thanks! Seems to work well enough.

This still feels like a bug in Navigator that should get solved properly. I'd consider re-opening the issue.

+1 for re-opening. We should know if this an intended behaviour or its a bug in navigator.

Yeah this should be a supported feature. I think it'd be hard to automatically infer if a scene is opaque but we could have a flag on the route.

I think this won't be fixed in the current version of Navigator -- a new version of Navigator is in progress though so stay tuned :)

@ide - shall we close this?

Hi there! This issue is being closed because it has been inactive for a while.

But don't worry, it will live on with ProductPains! Check out its new home: https://productpains.com/post/react-native/navigator-scene-with-transparent-background-doesnt-show-previous-scene

ProductPains helps the community prioritize the most important issues thanks to its voting feature.
It is easy to use - just login with GitHub.

Also, if this issue is a bug, please consider sending a PR with a fix.
We're a small team and rely on the community for bug fixes of issues that don't affect fb apps.

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.

``` js
{
// 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',
         },
     }),
   },

},
```

  1. 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.

``` js
_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?

I tweaked the above comment a bit and submitted it as a #7682. Changing the scene configs isn't _necessary_, though arguably nice to have. I left it out of that PR since I didn't want to potentially duplicate each Float* config just to remove the scaling transition.

@seansfkelley Please see the PR through, this is very useful :)

@dragonwong I thoughts, to change the duration of Navigator's animation should be updating the file of
CustomComponents/Navigator/NavigatorSceneConfigs.js

ModalFloatFromBottom: {
    ...BaseConfig,
  gestures: {},// none gestures handling
  //defaultTransitionVelocity: 2,

  springTension: 700,
  animationInterpolators: {
    into: buildStyleInterpolator(FromTheFront),
    out: buildStyleInterpolator(ModalPrevFixed),
  },
},

Additionally,
it is necessary to add '{isPreViewStatic: true}', such as this.props.navigator.push({pageType: "modal-pop", opts:{isPreViewStatic: true}}) , since the file CustomComponents/Navigator/Navigator.js is updated, nextRoute.opts.isPreViewStatic.

@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);
    }
  },

@samzanemesis I've tried your code. It work like a charm. Thanks to @dragonwong and others. You all are awesome guys.

@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 @ivanbrens Thanks for your solutions, it works, resolve my questions, I hope RN will support it in the future, so that we do not need to change source code.

Was this page helpful?
0 / 5 - 0 ratings