mirror of
https://github.com/status-im/react-native.git
synced 2025-01-28 02:04:55 +00:00
[ReactNative] Navigator refactor and improvements
This commit is contained in:
parent
473acdbc74
commit
8afdf30363
@ -315,13 +315,11 @@ var Navigator = React.createClass({
|
||||
// On first render, we will render every scene in the initialRouteStack
|
||||
updatingRangeStart: 0,
|
||||
updatingRangeLength: routeStack.length,
|
||||
// Either animating or gesturing.
|
||||
isAnimating: false,
|
||||
jumpToIndex: routeStack.length - 1,
|
||||
presentedIndex: initialRouteIndex,
|
||||
isResponderOnlyToBlockTouches: false,
|
||||
fromIndex: initialRouteIndex,
|
||||
toIndex: initialRouteIndex,
|
||||
transitionFromIndex: null,
|
||||
activeGesture: null,
|
||||
pendingGestureProgress: null,
|
||||
transitionQueue: [],
|
||||
};
|
||||
},
|
||||
|
||||
@ -356,9 +354,24 @@ var Navigator = React.createClass({
|
||||
popToTop: this.popToTop,
|
||||
};
|
||||
this._handlers = {};
|
||||
|
||||
this.springSystem = new rebound.SpringSystem();
|
||||
this.spring = this.springSystem.createSpring();
|
||||
this.spring.setRestSpeedThreshold(0.05);
|
||||
this.spring.setCurrentValue(0).setAtRest();
|
||||
this.spring.addListener({
|
||||
onSpringEndStateChange: () => {
|
||||
if (!this._interactionHandle) {
|
||||
this._interactionHandle = this.createInteractionHandle();
|
||||
}
|
||||
},
|
||||
onSpringUpdate: () => {
|
||||
this._handleSpringUpdate();
|
||||
},
|
||||
onSpringAtRest: () => {
|
||||
this._completeTransition();
|
||||
},
|
||||
});
|
||||
this.panGesture = PanResponder.create({
|
||||
onStartShouldSetPanResponderCapture: this._handleStartShouldSetPanResponderCapture,
|
||||
onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
|
||||
onPanResponderGrant: this._handlePanResponderGrant,
|
||||
onPanResponderRelease: this._handlePanResponderRelease,
|
||||
@ -427,20 +440,8 @@ var Navigator = React.createClass({
|
||||
this._handlers[this.state.routeStack.indexOf(route)] = handler;
|
||||
},
|
||||
|
||||
_configureSpring: function(animationConfig) {
|
||||
var config = this.spring.getSpringConfig();
|
||||
config.friction = animationConfig.springFriction;
|
||||
config.tension = animationConfig.springTension;
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.springSystem = new rebound.SpringSystem();
|
||||
this.spring = this.springSystem.createSpring();
|
||||
this.spring.setRestSpeedThreshold(0.05);
|
||||
var animationConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
animationConfig && this._configureSpring(animationConfig);
|
||||
this.spring.addListener(this);
|
||||
this.onSpringUpdate();
|
||||
this._handleSpringUpdate();
|
||||
this._emitDidFocus(this.state.routeStack[this.state.presentedIndex]);
|
||||
if (this.parentNavigator) {
|
||||
this.parentNavigator.setHandler(this._handleRequest);
|
||||
@ -484,98 +485,113 @@ var Navigator = React.createClass({
|
||||
updatingRangeStart: 0,
|
||||
updatingRangeLength: nextRouteStack.length,
|
||||
presentedIndex: destIndex,
|
||||
jumpToIndex: destIndex,
|
||||
toIndex: destIndex,
|
||||
fromIndex: destIndex,
|
||||
activeGesture: null,
|
||||
transitionFromIndex: null,
|
||||
transitionQueue: [],
|
||||
}, () => {
|
||||
this.onSpringUpdate();
|
||||
this._handleSpringUpdate();
|
||||
});
|
||||
},
|
||||
|
||||
_transitionTo: function(destIndex, velocity, jumpSpringTo, cb) {
|
||||
if (destIndex === this.state.presentedIndex) {
|
||||
return;
|
||||
}
|
||||
if (this.state.transitionFromIndex !== null) {
|
||||
this.state.transitionQueue.push({
|
||||
destIndex,
|
||||
velocity,
|
||||
cb,
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.state.transitionFromIndex = this.state.presentedIndex;
|
||||
this.state.presentedIndex = destIndex;
|
||||
this.state.transitionCb = cb;
|
||||
this._onAnimationStart();
|
||||
if (AnimationsDebugModule) {
|
||||
AnimationsDebugModule.startRecordingFps();
|
||||
}
|
||||
var sceneConfig = this.state.sceneConfigStack[this.state.transitionFromIndex] ||
|
||||
this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
invariant(
|
||||
sceneConfig,
|
||||
'Cannot configure scene at index ' + this.state.transitionFromIndex
|
||||
);
|
||||
if (jumpSpringTo != null) {
|
||||
this.spring.setCurrentValue(jumpSpringTo);
|
||||
}
|
||||
this.spring.setOvershootClampingEnabled(true);
|
||||
this.spring.getSpringConfig().friction = sceneConfig.springFriction;
|
||||
this.spring.getSpringConfig().tension = sceneConfig.springTension;
|
||||
this.spring.setVelocity(velocity || sceneConfig.defaultTransitionVelocity);
|
||||
this.spring.setEndValue(1);
|
||||
var willFocusRoute = this._subRouteFocus[this.state.presentedIndex] || this.state.routeStack[this.state.presentedIndex];
|
||||
this._emitWillFocus(willFocusRoute);
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO: Accept callback for spring completion.
|
||||
* This happens for each frame of either a gesture or a transition. If both are
|
||||
* happening, we only set values for the transition and the gesture will catch up later
|
||||
*/
|
||||
_requestTransitionTo: function(topOfStack) {
|
||||
if (topOfStack !== this.state.presentedIndex) {
|
||||
invariant(!this.state.isAnimating, 'Cannot navigate while transitioning');
|
||||
this.state.fromIndex = this.state.presentedIndex;
|
||||
this.state.toIndex = topOfStack;
|
||||
this.spring.setOvershootClampingEnabled(false);
|
||||
if (AnimationsDebugModule) {
|
||||
AnimationsDebugModule.startRecordingFps();
|
||||
}
|
||||
this._transitionToToIndexWithVelocity(
|
||||
this.state.sceneConfigStack[this.state.fromIndex].defaultTransitionVelocity
|
||||
_handleSpringUpdate: function() {
|
||||
// Prioritize handling transition in progress over a gesture:
|
||||
if (this.state.transitionFromIndex != null) {
|
||||
this._transitionBetween(
|
||||
this.state.transitionFromIndex,
|
||||
this.state.presentedIndex,
|
||||
this.spring.getCurrentValue()
|
||||
);
|
||||
} else if (this.state.activeGesture != null) {
|
||||
this._transitionBetween(
|
||||
this.state.presentedIndex,
|
||||
this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture),
|
||||
this.spring.getCurrentValue()
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* `onSpring*` spring delegate. Wired up via `spring.addListener(this)`
|
||||
* This happens at the end of a transition started by transitionTo
|
||||
*/
|
||||
onSpringEndStateChange: function() {
|
||||
if (!this._interactionHandle) {
|
||||
this._interactionHandle = this.createInteractionHandle();
|
||||
_completeTransition: function() {
|
||||
if (this.spring.getCurrentValue() !== 1) {
|
||||
if (this.state.pendingGestureProgress) {
|
||||
this.state.pendingGestureProgress = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
onSpringUpdate: function() {
|
||||
this._transitionBetween(
|
||||
this.state.fromIndex,
|
||||
this.state.toIndex,
|
||||
this.spring.getCurrentValue()
|
||||
);
|
||||
},
|
||||
|
||||
onSpringAtRest: function() {
|
||||
this.state.isAnimating = false;
|
||||
this._completeTransition();
|
||||
this._onAnimationEnd();
|
||||
var presentedIndex = this.state.presentedIndex;
|
||||
var didFocusRoute = this._subRouteFocus[presentedIndex] || this.state.routeStack[presentedIndex];
|
||||
this._emitDidFocus(didFocusRoute);
|
||||
if (AnimationsDebugModule) {
|
||||
AnimationsDebugModule.stopRecordingFps(Date.now());
|
||||
}
|
||||
this.state.transitionFromIndex = null;
|
||||
this.spring.setCurrentValue(0).setAtRest();
|
||||
this._hideScenes();
|
||||
if (this.state.transitionCb) {
|
||||
this.state.transitionCb();
|
||||
this.state.transitionCb = null;
|
||||
}
|
||||
if (this._interactionHandle) {
|
||||
this.clearInteractionHandle(this._interactionHandle);
|
||||
this._interactionHandle = null;
|
||||
}
|
||||
},
|
||||
|
||||
_completeTransition: function() {
|
||||
if (this.spring.getCurrentValue() === 1) {
|
||||
this._onAnimationEnd();
|
||||
var presentedIndex = this.state.toIndex;
|
||||
this.state.presentedIndex = presentedIndex;
|
||||
this.state.fromIndex = presentedIndex;
|
||||
var didFocusRoute = this._subRouteFocus[presentedIndex] || this.state.routeStack[presentedIndex];
|
||||
this._emitDidFocus(didFocusRoute);
|
||||
this._removePoppedRoutes();
|
||||
if (AnimationsDebugModule) {
|
||||
AnimationsDebugModule.stopRecordingFps(Date.now());
|
||||
}
|
||||
} else {
|
||||
this.state.fromIndex = this.state.presentedIndex;
|
||||
this.state.toIndex = this.state.presentedIndex;
|
||||
if (this.state.pendingGestureProgress) {
|
||||
this.spring.setEndValue(this.state.pendingGestureProgress);
|
||||
return;
|
||||
}
|
||||
if (this.state.transitionQueue.length) {
|
||||
var queuedTransition = this.state.transitionQueue.shift();
|
||||
this._transitionTo(
|
||||
queuedTransition.destIndex,
|
||||
queuedTransition.velocity,
|
||||
null,
|
||||
queuedTransition.cb
|
||||
);
|
||||
}
|
||||
this._hideOtherScenes(this.state.presentedIndex);
|
||||
},
|
||||
|
||||
_transitionToToIndexWithVelocity: function(v) {
|
||||
this._configureSpring(
|
||||
// For visual consistency, the from index is always used to configure the spring
|
||||
this.state.sceneConfigStack[this.state.fromIndex]
|
||||
);
|
||||
this._onAnimationStart();
|
||||
this.state.isAnimating = true;
|
||||
this.spring.setVelocity(v);
|
||||
this.spring.setEndValue(1);
|
||||
var willFocusRoute = this._subRouteFocus[this.state.toIndex] || this.state.routeStack[this.state.toIndex];
|
||||
this._emitWillFocus(willFocusRoute);
|
||||
},
|
||||
|
||||
_transitionToFromIndexWithVelocity: function(v) {
|
||||
this._configureSpring(
|
||||
this.state.sceneConfigStack[this.state.fromIndex]
|
||||
);
|
||||
this.state.isAnimating = true;
|
||||
this.spring.setVelocity(v);
|
||||
this.spring.setEndValue(0);
|
||||
},
|
||||
|
||||
_emitDidFocus: function(route) {
|
||||
@ -609,9 +625,11 @@ var Navigator = React.createClass({
|
||||
/**
|
||||
* Does not delete the scenes - merely hides them.
|
||||
*/
|
||||
_hideOtherScenes: function(activeIndex) {
|
||||
_hideScenes: function() {
|
||||
for (var i = 0; i < this.state.routeStack.length; i++) {
|
||||
if (i === activeIndex) {
|
||||
// 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;
|
||||
@ -621,22 +639,31 @@ var Navigator = React.createClass({
|
||||
},
|
||||
|
||||
_onAnimationStart: function() {
|
||||
this._setRenderSceneToHarwareTextureAndroid(this.state.fromIndex, true);
|
||||
this._setRenderSceneToHarwareTextureAndroid(this.state.toIndex, true);
|
||||
var fromIndex = this.state.presentedIndex;
|
||||
var toIndex = this.state.presentedIndex;
|
||||
if (this.state.transitionFromIndex != null) {
|
||||
fromIndex = this.state.transitionFromIndex;
|
||||
} else if (this.state.activeGesture) {
|
||||
toIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
|
||||
}
|
||||
this._setRenderSceneToHarwareTextureAndroid(fromIndex, true);
|
||||
this._setRenderSceneToHarwareTextureAndroid(toIndex, true);
|
||||
|
||||
var navBar = this._navBar;
|
||||
if (navBar && navBar.onAnimationStart) {
|
||||
navBar.onAnimationStart(this.state.fromIndex, this.state.toIndex);
|
||||
navBar.onAnimationStart(fromIndex, toIndex);
|
||||
}
|
||||
},
|
||||
|
||||
_onAnimationEnd: function() {
|
||||
this._setRenderSceneToHarwareTextureAndroid(this.state.fromIndex, false);
|
||||
this._setRenderSceneToHarwareTextureAndroid(this.state.toIndex, false);
|
||||
var max = this.state.routeStack.length - 1;
|
||||
for (var index = 0; index <= max; index++) {
|
||||
this._setRenderSceneToHarwareTextureAndroid(index, false);
|
||||
}
|
||||
|
||||
var navBar = this._navBar;
|
||||
if (navBar && navBar.onAnimationEnd) {
|
||||
navBar.onAnimationEnd(this.state.fromIndex, this.state.toIndex);
|
||||
navBar.onAnimationEnd();
|
||||
}
|
||||
},
|
||||
|
||||
@ -648,16 +675,6 @@ var Navigator = React.createClass({
|
||||
viewAtIndex.setNativeProps({renderToHardwareTextureAndroid: shouldRenderToHardwareTexture});
|
||||
},
|
||||
|
||||
/**
|
||||
* Becomes the responder on touch start (capture) while animating so that it
|
||||
* blocks all touch interactions inside of it. However, this responder lock
|
||||
* means nothing more than that. We record if the sole reason for being
|
||||
* responder is to block interactions (`isResponderOnlyToBlockTouches`).
|
||||
*/
|
||||
_handleStartShouldSetPanResponderCapture: function(e, gestureState) {
|
||||
return this.state.isAnimating;
|
||||
},
|
||||
|
||||
_handleMoveShouldSetPanResponder: function(e, gestureState) {
|
||||
var currentRoute = this.state.routeStack[this.state.presentedIndex];
|
||||
var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
@ -675,18 +692,12 @@ var Navigator = React.createClass({
|
||||
|
||||
_handlePanResponderGrant: function(e, gestureState) {
|
||||
invariant(
|
||||
this._expectingGestureGrant || this.state.isAnimating,
|
||||
this._expectingGestureGrant,
|
||||
'Responder granted unexpectedly.'
|
||||
);
|
||||
this._activeGestureAction = this._expectingGestureGrant;
|
||||
this._attachGesture(this._expectingGestureGrant);
|
||||
this._onAnimationStart();
|
||||
this._expectingGestureGrant = null;
|
||||
this.state.isResponderOnlyToBlockTouches = this.state.isAnimating;
|
||||
if (!this.state.isAnimating) {
|
||||
this.state.fromIndex = this.state.presentedIndex;
|
||||
var gestureSceneDelta = this._deltaForGestureAction(this._activeGestureAction);
|
||||
this.state.toIndex = this.state.presentedIndex + gestureSceneDelta;
|
||||
this._onAnimationStart();
|
||||
}
|
||||
},
|
||||
|
||||
_deltaForGestureAction: function(gestureAction) {
|
||||
@ -704,13 +715,9 @@ var Navigator = React.createClass({
|
||||
|
||||
_handlePanResponderRelease: function(e, gestureState) {
|
||||
var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
var releaseGestureAction = this._activeGestureAction;
|
||||
this._activeGestureAction = null;
|
||||
if (this.state.isResponderOnlyToBlockTouches) {
|
||||
this.state.isResponderOnlyToBlockTouches = false;
|
||||
return;
|
||||
}
|
||||
var releaseGestureAction = this.state.activeGesture;
|
||||
var releaseGesture = sceneConfig.gestures[releaseGestureAction];
|
||||
var destIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
|
||||
if (this.spring.getCurrentValue() === 0) {
|
||||
// The spring is at zero, so the gesture is already complete
|
||||
this.spring.setCurrentValue(0).setAtRest();
|
||||
@ -733,38 +740,86 @@ var Navigator = React.createClass({
|
||||
var hasGesturedEnoughToComplete = gestureDistance > releaseGesture.fullDistance * releaseGesture.stillCompletionRatio;
|
||||
transitionVelocity = hasGesturedEnoughToComplete ? releaseGesture.snapVelocity : -releaseGesture.snapVelocity;
|
||||
}
|
||||
this.spring.setOvershootClampingEnabled(true);
|
||||
if (transitionVelocity < 0 || this._doesGestureOverswipe(releaseGestureAction)) {
|
||||
this._transitionToFromIndexWithVelocity(transitionVelocity);
|
||||
// This gesture is to an overswiped region or does not have enough velocity to complete
|
||||
// If we are currently mid-transition, then this gesture was a pending gesture. Because this gesture takes no action, we can stop here
|
||||
if (this.state.transitionFromIndex == null) {
|
||||
// There is no current transition, so we need to transition back to the presented index
|
||||
var transitionBackToPresentedIndex = this.state.presentedIndex;
|
||||
// slight hack: change the presented index for a moment in order to transitionTo correctly
|
||||
this.state.presentedIndex = destIndex;
|
||||
this._transitionTo(
|
||||
transitionBackToPresentedIndex,
|
||||
- transitionVelocity,
|
||||
1 - this.spring.getCurrentValue()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this._transitionToToIndexWithVelocity(transitionVelocity);
|
||||
// The gesture has enough velocity to complete, so we transition to the gesture's destination
|
||||
this._transitionTo(destIndex, transitionVelocity);
|
||||
}
|
||||
this._detachGesture();
|
||||
},
|
||||
|
||||
_handlePanResponderTerminate: function(e, gestureState) {
|
||||
this._activeGestureAction = null;
|
||||
this.state.isResponderOnlyToBlockTouches = false;
|
||||
this._transitionToFromIndexWithVelocity(0);
|
||||
var destIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
|
||||
this._detachGesture();
|
||||
var transitionBackToPresentedIndex = this.state.presentedIndex;
|
||||
// slight hack: change the presented index for a moment in order to transitionTo correctly
|
||||
this.state.presentedIndex = destIndex;
|
||||
this._transitionTo(
|
||||
transitionBackToPresentedIndex,
|
||||
null,
|
||||
1 - this.spring.getCurrentValue()
|
||||
);
|
||||
},
|
||||
|
||||
_attachGesture: function(gestureId) {
|
||||
this.state.activeGesture = gestureId;
|
||||
},
|
||||
|
||||
_detachGesture: function() {
|
||||
this.state.activeGesture = null;
|
||||
this.state.pendingGestureProgress = null;
|
||||
this._hideScenes();
|
||||
},
|
||||
|
||||
_handlePanResponderMove: function(e, gestureState) {
|
||||
if (!this.state.isResponderOnlyToBlockTouches) {
|
||||
var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
var gesture = sceneConfig.gestures[this._activeGestureAction];
|
||||
var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top';
|
||||
var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top';
|
||||
var distance = isTravelVertical ? gestureState.dy : gestureState.dx;
|
||||
distance = isTravelInverted ? - distance : distance;
|
||||
var gestureDetectMovement = gesture.gestureDetectMovement;
|
||||
var nextProgress = (distance - gestureDetectMovement) /
|
||||
(gesture.fullDistance - gestureDetectMovement);
|
||||
if (this._doesGestureOverswipe(this._activeGestureAction)) {
|
||||
var frictionConstant = gesture.overswipe.frictionConstant;
|
||||
var frictionByDistance = gesture.overswipe.frictionByDistance;
|
||||
var frictionRatio = 1 / ((frictionConstant) + (Math.abs(nextProgress) * frictionByDistance));
|
||||
nextProgress *= frictionRatio;
|
||||
}
|
||||
this.spring.setCurrentValue(clamp(0, nextProgress, 1));
|
||||
var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
|
||||
if (this.state.activeGesture) {
|
||||
var gesture = sceneConfig.gestures[this.state.activeGesture];
|
||||
return this._moveAttachedGesture(gesture, gestureState);
|
||||
}
|
||||
var matchedGesture = this._matchGestureAction(sceneConfig.gestures, gestureState);
|
||||
if (matchedGesture) {
|
||||
this._attachGesture(matchedGesture);
|
||||
}
|
||||
},
|
||||
|
||||
_moveAttachedGesture: function(gesture, gestureState) {
|
||||
var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top';
|
||||
var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top';
|
||||
var distance = isTravelVertical ? gestureState.dy : gestureState.dx;
|
||||
distance = isTravelInverted ? - distance : distance;
|
||||
var gestureDetectMovement = gesture.gestureDetectMovement;
|
||||
var nextProgress = (distance - gestureDetectMovement) /
|
||||
(gesture.fullDistance - gestureDetectMovement);
|
||||
if (nextProgress < 0 && gesture.isDetachable) {
|
||||
this._detachGesture();
|
||||
}
|
||||
if (this._doesGestureOverswipe(this.state.activeGesture)) {
|
||||
var frictionConstant = gesture.overswipe.frictionConstant;
|
||||
var frictionByDistance = gesture.overswipe.frictionByDistance;
|
||||
var frictionRatio = 1 / ((frictionConstant) + (Math.abs(nextProgress) * frictionByDistance));
|
||||
nextProgress *= frictionRatio;
|
||||
}
|
||||
nextProgress = clamp(0, nextProgress, 1);
|
||||
if (this.state.transitionFromIndex != null) {
|
||||
this.state.pendingGestureProgress = nextProgress;
|
||||
} else if (this.state.pendingGestureProgress) {
|
||||
this.spring.setEndValue(nextProgress);
|
||||
} else {
|
||||
this.spring.setCurrentValue(nextProgress);
|
||||
}
|
||||
},
|
||||
|
||||
@ -772,9 +827,6 @@ var Navigator = React.createClass({
|
||||
if (!gestures) {
|
||||
return null;
|
||||
}
|
||||
if (this.state.isResponderOnlyToBlockTouches || this.state.isAnimating) {
|
||||
return null;
|
||||
}
|
||||
var matchedGesture = null;
|
||||
GESTURE_ACTIONS.some((gestureName) => {
|
||||
var gesture = gestures[gestureName];
|
||||
@ -815,7 +867,7 @@ var Navigator = React.createClass({
|
||||
return;
|
||||
}
|
||||
// Use toIndex animation when we move forwards. Use fromIndex when we move back
|
||||
var sceneConfigIndex = this.state.presentedIndex < toIndex ? toIndex : fromIndex;
|
||||
var sceneConfigIndex = fromIndex < toIndex ? toIndex : fromIndex;
|
||||
var sceneConfig = this.state.sceneConfigStack[sceneConfigIndex];
|
||||
// this happens for overswiping when there is no scene at toIndex
|
||||
if (!sceneConfig) {
|
||||
@ -850,10 +902,6 @@ var Navigator = React.createClass({
|
||||
this.state.updatingRangeLength = this.state.routeStack.length;
|
||||
},
|
||||
|
||||
_canNavigate: function() {
|
||||
return !this.state.isAnimating;
|
||||
},
|
||||
|
||||
_getDestIndexWithinBounds: function(n) {
|
||||
var currentIndex = this.state.presentedIndex;
|
||||
var destIndex = currentIndex + n;
|
||||
@ -871,17 +919,13 @@ var Navigator = React.createClass({
|
||||
|
||||
_jumpN: function(n) {
|
||||
var destIndex = this._getDestIndexWithinBounds(n);
|
||||
if (!this._canNavigate()) {
|
||||
return; // It's busy animating or transitioning.
|
||||
}
|
||||
var requestTransitionAndResetUpdatingRange = () => {
|
||||
this._requestTransitionTo(destIndex);
|
||||
this._transitionTo(destIndex);
|
||||
this._resetUpdatingRange();
|
||||
};
|
||||
this.setState({
|
||||
updatingRangeStart: destIndex,
|
||||
updatingRangeLength: 1,
|
||||
toIndex: destIndex,
|
||||
}, requestTransitionAndResetUpdatingRange);
|
||||
},
|
||||
|
||||
@ -904,9 +948,6 @@ var Navigator = React.createClass({
|
||||
|
||||
push: function(route) {
|
||||
invariant(!!route, 'Must supply route to push');
|
||||
if (!this._canNavigate()) {
|
||||
return; // It's busy animating or transitioning.
|
||||
}
|
||||
var activeLength = this.state.presentedIndex + 1;
|
||||
var activeStack = this.state.routeStack.slice(0, activeLength);
|
||||
var activeIDStack = this.state.idStack.slice(0, activeLength);
|
||||
@ -917,34 +958,34 @@ var Navigator = React.createClass({
|
||||
this.props.configureScene(route),
|
||||
]);
|
||||
var requestTransitionAndResetUpdatingRange = () => {
|
||||
this._requestTransitionTo(nextStack.length - 1);
|
||||
this._transitionTo(nextStack.length - 1);
|
||||
this._resetUpdatingRange();
|
||||
};
|
||||
var navigationState = {
|
||||
toRoute: route,
|
||||
fromRoute: this.state.routeStack[this.state.routeStack.length - 1],
|
||||
};
|
||||
this.setState({
|
||||
idStack: nextIDStack,
|
||||
routeStack: nextStack,
|
||||
sceneConfigStack: nextAnimationConfigStack,
|
||||
jumpToIndex: nextStack.length - 1,
|
||||
updatingRangeStart: nextStack.length - 1,
|
||||
updatingRangeLength: 1,
|
||||
}, requestTransitionAndResetUpdatingRange);
|
||||
},
|
||||
|
||||
_popN: function(n) {
|
||||
if (n === 0 || !this._canNavigate()) {
|
||||
if (n === 0) {
|
||||
return;
|
||||
}
|
||||
invariant(
|
||||
this.state.presentedIndex - n >= 0,
|
||||
'Cannot pop below zero'
|
||||
);
|
||||
this.state.jumpToIndex = this.state.presentedIndex - n;
|
||||
this._requestTransitionTo(
|
||||
this.state.presentedIndex - n
|
||||
var popIndex = this.state.presentedIndex - n;
|
||||
this._transitionTo(
|
||||
popIndex,
|
||||
null, // default velocity
|
||||
null, // no spring jumping
|
||||
() => {
|
||||
this._cleanScenesPastIndex(popIndex);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
@ -1026,7 +1067,7 @@ var Navigator = React.createClass({
|
||||
},
|
||||
|
||||
replacePreviousAndPop: function(route) {
|
||||
if (this.state.routeStack.length < 2 || !this._canNavigate()) {
|
||||
if (this.state.routeStack.length < 2) {
|
||||
return;
|
||||
}
|
||||
this.replacePrevious(route);
|
||||
@ -1035,11 +1076,9 @@ var Navigator = React.createClass({
|
||||
|
||||
resetTo: function(route) {
|
||||
invariant(!!route, 'Must supply route to push');
|
||||
if (this._canNavigate()) {
|
||||
this.replaceAtIndex(route, 0, () => {
|
||||
this.popToRoute(route);
|
||||
});
|
||||
}
|
||||
this.replaceAtIndex(route, 0, () => {
|
||||
this.popToRoute(route);
|
||||
});
|
||||
},
|
||||
|
||||
getCurrentRoutes: function() {
|
||||
@ -1055,8 +1094,8 @@ var Navigator = React.createClass({
|
||||
this.props.onItemRef && this.props.onItemRef(ref, itemIndex);
|
||||
},
|
||||
|
||||
_removePoppedRoutes: function() {
|
||||
var newStackLength = this.state.jumpToIndex + 1;
|
||||
_cleanScenesPastIndex: function(index) {
|
||||
var newStackLength = index + 1;
|
||||
// Remove any unneeded rendered routes.
|
||||
if (newStackLength < this.state.routeStack.length) {
|
||||
var updatingRangeStart = newStackLength; // One past the top
|
||||
|
@ -33,6 +33,8 @@ var StaticContainer = require('StaticContainer.react');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var View = require('View');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
var Interpolators = NavigatorBreadcrumbNavigationBarStyles.Interpolators;
|
||||
var PropTypes = React.PropTypes;
|
||||
|
||||
@ -99,6 +101,10 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({
|
||||
var oldDistToCenter = index - fromIndex;
|
||||
var newDistToCenter = index - toIndex;
|
||||
var interpolate;
|
||||
invariant(
|
||||
Interpolators[index],
|
||||
'Cannot find breadcrumb interpolators for ' + index
|
||||
);
|
||||
if (oldDistToCenter > 0 && newDistToCenter === 0 ||
|
||||
newDistToCenter > 0 && oldDistToCenter === 0) {
|
||||
interpolate = Interpolators[index].RightToCenter;
|
||||
@ -146,10 +152,9 @@ var NavigatorBreadcrumbNavigationBar = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
onAnimationEnd: function(fromIndex, toIndex) {
|
||||
var max = Math.max(fromIndex, toIndex);
|
||||
var min = Math.min(fromIndex, toIndex);
|
||||
for (var index = min; index <= max; index++) {
|
||||
onAnimationEnd: function() {
|
||||
var max = this.props.navState.routeStack.length - 1;
|
||||
for (var index = 0; index <= max; index++) {
|
||||
this._setRenderViewsToHardwareTextureAndroid(index, false);
|
||||
}
|
||||
},
|
||||
|
@ -243,6 +243,9 @@ var BaseOverswipeConfig = {
|
||||
|
||||
var BaseLeftToRightGesture = {
|
||||
|
||||
// If the gesture can end and restart during one continuous touch
|
||||
isDetachable: false,
|
||||
|
||||
// How far the swipe must drag to start transitioning
|
||||
gestureDetectMovement: 2,
|
||||
|
||||
@ -323,11 +326,13 @@ var NavigatorSceneConfigs = {
|
||||
...BaseLeftToRightGesture,
|
||||
overswipe: BaseOverswipeConfig,
|
||||
edgeHitWidth: null,
|
||||
isDetachable: true,
|
||||
},
|
||||
jumpForward: {
|
||||
...BaseRightToLeftGesture,
|
||||
overswipe: BaseOverswipeConfig,
|
||||
edgeHitWidth: null,
|
||||
isDetachable: true,
|
||||
},
|
||||
},
|
||||
animationInterpolators: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user