mirror of
https://github.com/status-im/react-native.git
synced 2025-02-26 08:05:34 +00:00
[ReactNative] Fix Navigator scene hiding logic
Summary: Scenes with 0 opacity are being rendered on top of other scenes with full absolute positioning. On iOS this is fine, because the platform will not send touch events to a view with no opacity. On Android is poses a problem because the view on top, even with no opacity, is catching the touch events for the presented scene below. This change enhances the scene enabling and hiding logic, has better naming, and improves the documentation of it. @public Test Plan: Tested transitions and gestures in slow motion on iOS and Android
This commit is contained in:
parent
c5a6ec5b53
commit
548a0a6a4f
@ -28,6 +28,7 @@
|
||||
|
||||
var AnimationsDebugModule = require('NativeModules').AnimationsDebugModule;
|
||||
var BackAndroid = require('BackAndroid');
|
||||
var Dimensions = require('Dimensions');
|
||||
var InteractionMixin = require('InteractionMixin');
|
||||
var NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar');
|
||||
var NavigatorInterceptor = require('NavigatorInterceptor');
|
||||
@ -39,6 +40,7 @@ var Platform = require('Platform');
|
||||
var React = require('React');
|
||||
var StaticContainer = require('StaticContainer.react');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var StyleSheetRegistry = require('StyleSheetRegistry');
|
||||
var Subscribable = require('Subscribable');
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
var View = require('View');
|
||||
@ -52,13 +54,37 @@ var rebound = require('rebound');
|
||||
|
||||
var PropTypes = React.PropTypes;
|
||||
|
||||
var OFF_SCREEN = {style: {opacity: 0}};
|
||||
var SCREEN_WIDTH = Dimensions.get('window').width;
|
||||
var SCENE_DISABLED_NATIVE_PROPS = {
|
||||
style: {
|
||||
left: SCREEN_WIDTH,
|
||||
opacity: 0,
|
||||
},
|
||||
};
|
||||
|
||||
var __uid = 0;
|
||||
function getuid() {
|
||||
return __uid++;
|
||||
}
|
||||
|
||||
function resolveStyle(styles) {
|
||||
// The styles for a scene are a prop in the style format, which can be an object,
|
||||
// a number (which refers to the StyleSheetRegistry), or an arry of objects/numbers.
|
||||
// This function resolves the actual style values so we can call setNativeProps with
|
||||
// matching styles.
|
||||
var resolvedStyle = {};
|
||||
if (!Array.isArray(styles)) {
|
||||
styles = [styles];
|
||||
}
|
||||
styles.forEach((style) => {
|
||||
if (typeof style === 'number') {
|
||||
style = StyleSheetRegistry.getStyleByID(style);
|
||||
}
|
||||
resolvedStyle = merge(resolvedStyle, style);
|
||||
});
|
||||
return resolvedStyle;
|
||||
}
|
||||
|
||||
// styles moved to the top of the file so getDefaultProps can refer to it
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
@ -72,7 +98,7 @@ var styles = StyleSheet.create({
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
},
|
||||
currentScene: {
|
||||
baseScene: {
|
||||
position: 'absolute',
|
||||
overflow: 'hidden',
|
||||
left: 0,
|
||||
@ -80,11 +106,8 @@ var styles = StyleSheet.create({
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
},
|
||||
futureScene: {
|
||||
overflow: 'hidden',
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
opacity: 0,
|
||||
disabledScene: {
|
||||
left: SCREEN_WIDTH,
|
||||
},
|
||||
transitioner: {
|
||||
flex: 1,
|
||||
@ -571,10 +594,12 @@ var Navigator = React.createClass({
|
||||
},
|
||||
|
||||
/**
|
||||
* This happens at the end of a transition started by transitionTo
|
||||
* This happens at the end of a transition started by transitionTo, and when the spring catches up to a pending gesture
|
||||
*/
|
||||
_completeTransition: function() {
|
||||
if (this.spring.getCurrentValue() !== 1) {
|
||||
// The spring has finished catching up to a gesture in progress. Remove the pending progress
|
||||
// and we will be in a normal activeGesture state
|
||||
if (this.state.pendingGestureProgress) {
|
||||
this.state.pendingGestureProgress = null;
|
||||
}
|
||||
@ -599,11 +624,16 @@ var Navigator = React.createClass({
|
||||
this._interactionHandle = null;
|
||||
}
|
||||
if (this.state.pendingGestureProgress) {
|
||||
// A transition completed, but there is already another gesture happening.
|
||||
// Enable the scene and set the spring to catch up with the new gesture
|
||||
var gestureToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
|
||||
this._enableScene(gestureToIndex);
|
||||
this.spring.setEndValue(this.state.pendingGestureProgress);
|
||||
return;
|
||||
}
|
||||
if (this.state.transitionQueue.length) {
|
||||
var queuedTransition = this.state.transitionQueue.shift();
|
||||
this._enableScene(queuedTransition.destIndex);
|
||||
this._transitionTo(
|
||||
queuedTransition.destIndex,
|
||||
queuedTransition.velocity,
|
||||
@ -644,21 +674,44 @@ var Navigator = React.createClass({
|
||||
},
|
||||
|
||||
/**
|
||||
* Does not delete the scenes - merely hides them.
|
||||
* Hides scenes that we are not currently on or transitioning from
|
||||
*/
|
||||
_hideScenes: function() {
|
||||
for (var i = 0; i < this.state.routeStack.length; i++) {
|
||||
// This gets called when we detach a gesture, so there will not be a
|
||||
// current gesture, but there might be a transition in progress
|
||||
if (i === this.state.presentedIndex || i === this.state.transitionFromIndex) {
|
||||
continue;
|
||||
}
|
||||
var sceneRef = 'scene_' + i;
|
||||
this.refs[sceneRef] &&
|
||||
this.refs['scene_' + i].setNativeProps(OFF_SCREEN);
|
||||
this._disableScene(i);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Push a scene off the screen, so that opacity:0 scenes will not block touches sent to the presented scenes
|
||||
*/
|
||||
_disableScene: function(sceneIndex) {
|
||||
this.refs['scene_' + sceneIndex] &&
|
||||
this.refs['scene_' + sceneIndex].setNativeProps(SCENE_DISABLED_NATIVE_PROPS);
|
||||
},
|
||||
|
||||
/**
|
||||
* Put the scene back into the state as defined by props.sceneStyle, so transitions can happen normally
|
||||
*/
|
||||
_enableScene: function(sceneIndex) {
|
||||
// First, determine what the defined styles are for scenes in this navigator
|
||||
var sceneStyle = resolveStyle(this.props.sceneStyle);
|
||||
// Then restore the left value for this scene
|
||||
var enabledSceneNativeProps = {
|
||||
left: sceneStyle.left,
|
||||
};
|
||||
if (sceneIndex !== this.state.transitionFromIndex) {
|
||||
// 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.opacity = 0;
|
||||
}
|
||||
this.refs['scene_' + sceneIndex] &&
|
||||
this.refs['scene_' + sceneIndex].setNativeProps(enabledSceneNativeProps);
|
||||
},
|
||||
|
||||
_onAnimationStart: function() {
|
||||
var fromIndex = this.state.presentedIndex;
|
||||
var toIndex = this.state.presentedIndex;
|
||||
@ -669,7 +722,6 @@ var Navigator = React.createClass({
|
||||
}
|
||||
this._setRenderSceneToHarwareTextureAndroid(fromIndex, true);
|
||||
this._setRenderSceneToHarwareTextureAndroid(toIndex, true);
|
||||
|
||||
var navBar = this._navBar;
|
||||
if (navBar && navBar.onAnimationStart) {
|
||||
navBar.onAnimationStart(fromIndex, toIndex);
|
||||
@ -801,6 +853,8 @@ var Navigator = React.createClass({
|
||||
|
||||
_attachGesture: function(gestureId) {
|
||||
this.state.activeGesture = gestureId;
|
||||
var gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
|
||||
this._enableScene(gesturingToIndex);
|
||||
},
|
||||
|
||||
_detachGesture: function() {
|
||||
@ -831,6 +885,7 @@ var Navigator = React.createClass({
|
||||
(gesture.fullDistance - gestureDetectMovement);
|
||||
if (nextProgress < 0 && gesture.isDetachable) {
|
||||
this._detachGesture();
|
||||
this.spring.setCurrentValue(0);
|
||||
}
|
||||
if (this._doesGestureOverswipe(this.state.activeGesture)) {
|
||||
var frictionConstant = gesture.overswipe.frictionConstant;
|
||||
@ -945,6 +1000,7 @@ var Navigator = React.createClass({
|
||||
_jumpN: function(n) {
|
||||
var destIndex = this._getDestIndexWithinBounds(n);
|
||||
var requestTransitionAndResetUpdatingRange = () => {
|
||||
this._enableScene(destIndex);
|
||||
this._transitionTo(destIndex);
|
||||
this._resetUpdatingRange();
|
||||
};
|
||||
@ -978,12 +1034,14 @@ var Navigator = React.createClass({
|
||||
var activeIDStack = this.state.idStack.slice(0, activeLength);
|
||||
var activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
|
||||
var nextStack = activeStack.concat([route]);
|
||||
var destIndex = nextStack.length - 1;
|
||||
var nextIDStack = activeIDStack.concat([getuid()]);
|
||||
var nextAnimationConfigStack = activeAnimationConfigStack.concat([
|
||||
this.props.configureScene(route),
|
||||
]);
|
||||
var requestTransitionAndResetUpdatingRange = () => {
|
||||
this._transitionTo(nextStack.length - 1);
|
||||
this._enableScene(destIndex);
|
||||
this._transitionTo(destIndex);
|
||||
this._resetUpdatingRange();
|
||||
};
|
||||
this.setState({
|
||||
@ -1004,6 +1062,7 @@ var Navigator = React.createClass({
|
||||
'Cannot pop below zero'
|
||||
);
|
||||
var popIndex = this.state.presentedIndex - n;
|
||||
this._enableScene(popIndex);
|
||||
this._transitionTo(
|
||||
popIndex,
|
||||
null, // default velocity
|
||||
@ -1211,8 +1270,10 @@ var Navigator = React.createClass({
|
||||
route,
|
||||
sceneNavigatorContext
|
||||
);
|
||||
var initialSceneStyle = i === this.state.presentedIndex ?
|
||||
styles.currentScene : styles.futureScene;
|
||||
var disabledSceneStyle = null;
|
||||
if (i !== this.state.presentedIndex) {
|
||||
disabledSceneStyle = styles.disabledScene;
|
||||
}
|
||||
return (
|
||||
<View
|
||||
key={this.state.idStack[i]}
|
||||
@ -1220,7 +1281,7 @@ var Navigator = React.createClass({
|
||||
onStartShouldSetResponderCapture={() => {
|
||||
return i !== this.state.presentedIndex;
|
||||
}}
|
||||
style={[initialSceneStyle, this.props.sceneStyle]}>
|
||||
style={[styles.baseScene, this.props.sceneStyle, disabledSceneStyle]}>
|
||||
{React.cloneElement(child, {
|
||||
ref: this._handleItemRef.bind(null, this.state.idStack[i]),
|
||||
})}
|
||||
|
Loading…
x
Reference in New Issue
Block a user