mirror of
https://github.com/status-im/react-navigation-stack.git
synced 2025-01-14 16:44:34 +00:00
Optimize gesture logic, Reduce unnecessary creation of duplicates AnimatedNode
This commit is contained in:
parent
177ca217c9
commit
63df3e7290
@ -29,24 +29,7 @@ class StackView extends React.Component {
|
|||||||
this.props.onTransitionStart ||
|
this.props.onTransitionStart ||
|
||||||
this.props.navigationConfig.onTransitionStart
|
this.props.navigationConfig.onTransitionStart
|
||||||
}
|
}
|
||||||
onTransitionEnd={(transition, lastTransition) => {
|
onTransitionEnd={this._onTransitionEnd}
|
||||||
const { navigationConfig, navigation } = this.props;
|
|
||||||
const onTransitionEnd =
|
|
||||||
this.props.onTransitionEnd || navigationConfig.onTransitionEnd;
|
|
||||||
const transitionDestKey = transition.scene.route.key;
|
|
||||||
const isCurrentKey =
|
|
||||||
navigation.state.routes[navigation.state.index].key ===
|
|
||||||
transitionDestKey;
|
|
||||||
if (transition.navigation.state.isTransitioning && isCurrentKey) {
|
|
||||||
navigation.dispatch(
|
|
||||||
StackActions.completeTransition({
|
|
||||||
key: navigation.state.key,
|
|
||||||
toChildKey: transitionDestKey,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
onTransitionEnd && onTransitionEnd(transition, lastTransition);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -107,6 +90,26 @@ class StackView extends React.Component {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_onTransitionEnd = (transition, lastTransition) => {
|
||||||
|
const {
|
||||||
|
navigationConfig,
|
||||||
|
navigation,
|
||||||
|
onTransitionEnd = navigationConfig.onTransitionEnd,
|
||||||
|
} = this.props;
|
||||||
|
const transitionDestKey = transition.scene.route.key;
|
||||||
|
const isCurrentKey =
|
||||||
|
navigation.state.routes[navigation.state.index].key === transitionDestKey;
|
||||||
|
if (transition.navigation.state.isTransitioning && isCurrentKey) {
|
||||||
|
navigation.dispatch(
|
||||||
|
StackActions.completeTransition({
|
||||||
|
key: navigation.state.key,
|
||||||
|
toChildKey: transitionDestKey,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
onTransitionEnd && onTransitionEnd(transition, lastTransition);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default StackView;
|
export default StackView;
|
||||||
|
@ -84,14 +84,6 @@ const getDefaultHeaderHeight = isLandscape => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class StackViewLayout extends React.Component {
|
class StackViewLayout extends React.Component {
|
||||||
/**
|
|
||||||
* Used to identify the starting point of the position when the gesture starts, such that it can
|
|
||||||
* be updated according to its relative position. This means that a card can effectively be
|
|
||||||
* "caught"- If a gesture starts while a card is animating, the card does not jump into a
|
|
||||||
* corresponding location for the touch.
|
|
||||||
*/
|
|
||||||
_gestureStartValue = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* immediateIndex is used to represent the expected index that we will be on after a
|
* immediateIndex is used to represent the expected index that we will be on after a
|
||||||
* transition. To achieve a smooth animation when swiping back, the action to go back
|
* transition. To achieve a smooth animation when swiping back, the action to go back
|
||||||
@ -106,6 +98,28 @@ class StackViewLayout extends React.Component {
|
|||||||
this.panGestureRef = React.createRef();
|
this.panGestureRef = React.createRef();
|
||||||
this.gestureX = new Animated.Value(0);
|
this.gestureX = new Animated.Value(0);
|
||||||
this.gestureY = new Animated.Value(0);
|
this.gestureY = new Animated.Value(0);
|
||||||
|
this.positionSwitch = new Animated.Value(1);
|
||||||
|
if (Animated.subtract) {
|
||||||
|
this.gestureSwitch = Animated.subtract(1, this.positionSwitch);
|
||||||
|
} else {
|
||||||
|
this.gestureSwitch = Animated.add(
|
||||||
|
1,
|
||||||
|
Animated.multiply(-1, this.positionSwitch)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.gestureEvent = Animated.event(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
nativeEvent: {
|
||||||
|
translationX: this.gestureX,
|
||||||
|
translationY: this.gestureY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
useNativeDriver: USE_NATIVE_DRIVER,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
// Used when card's header is null and mode is float to make transition
|
// Used when card's header is null and mode is float to make transition
|
||||||
@ -114,7 +128,6 @@ class StackViewLayout extends React.Component {
|
|||||||
// on mount what the header height is so we have just used the most
|
// on mount what the header height is so we have just used the most
|
||||||
// common cases here.
|
// common cases here.
|
||||||
floatingHeaderHeight: getDefaultHeaderHeight(props.isLandscape),
|
floatingHeaderHeight: getDefaultHeaderHeight(props.isLandscape),
|
||||||
gesturePosition: null,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,9 +158,9 @@ class StackViewLayout extends React.Component {
|
|||||||
headerTitleInterpolator,
|
headerTitleInterpolator,
|
||||||
headerRightInterpolator,
|
headerRightInterpolator,
|
||||||
headerBackgroundInterpolator,
|
headerBackgroundInterpolator,
|
||||||
} = this._getTransitionConfig();
|
} = this._transitionConfig;
|
||||||
|
|
||||||
let backgroundTransitionPresetInterpolator = this._getHeaderBackgroundTransitionPreset();
|
const backgroundTransitionPresetInterpolator = this._getHeaderBackgroundTransitionPreset();
|
||||||
if (backgroundTransitionPresetInterpolator) {
|
if (backgroundTransitionPresetInterpolator) {
|
||||||
headerBackgroundInterpolator = backgroundTransitionPresetInterpolator;
|
headerBackgroundInterpolator = backgroundTransitionPresetInterpolator;
|
||||||
}
|
}
|
||||||
@ -159,7 +172,7 @@ class StackViewLayout extends React.Component {
|
|||||||
{renderHeader({
|
{renderHeader({
|
||||||
...passProps,
|
...passProps,
|
||||||
...transitionProps,
|
...transitionProps,
|
||||||
position: this._getPosition(),
|
position: this.position,
|
||||||
scene,
|
scene,
|
||||||
mode: headerMode,
|
mode: headerMode,
|
||||||
transitionPreset: this._getHeaderTransitionPreset(),
|
transitionPreset: this._getHeaderTransitionPreset(),
|
||||||
@ -240,15 +253,38 @@ class StackViewLayout extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onFloatingHeaderLayout = e => {
|
_onFloatingHeaderLayout = e => {
|
||||||
this.setState({ floatingHeaderHeight: e.nativeEvent.layout.height });
|
const { height } = e.nativeEvent.layout;
|
||||||
|
if (height !== this.state.floatingHeaderHeight) {
|
||||||
|
this.setState({ floatingHeaderHeight: height });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
_prepareAnimated() {
|
||||||
let floatingHeader = null;
|
if (this.props === this._prevProps) {
|
||||||
const headerMode = this._getHeaderMode();
|
return;
|
||||||
|
}
|
||||||
|
this._prevProps = this.props;
|
||||||
|
|
||||||
|
this._prepareGesture();
|
||||||
|
this._preparePosition();
|
||||||
|
this._prepareTransitionConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this._prepareAnimated();
|
||||||
|
|
||||||
|
const { transitionProps } = this.props;
|
||||||
|
const {
|
||||||
|
navigation: {
|
||||||
|
state: { index },
|
||||||
|
},
|
||||||
|
scenes,
|
||||||
|
} = transitionProps;
|
||||||
|
|
||||||
|
const headerMode = this._getHeaderMode();
|
||||||
|
let floatingHeader = null;
|
||||||
if (headerMode === 'float') {
|
if (headerMode === 'float') {
|
||||||
const { scene } = this.props.transitionProps;
|
const { scene } = transitionProps;
|
||||||
floatingHeader = (
|
floatingHeader = (
|
||||||
<View
|
<View
|
||||||
style={styles.floatingHeader}
|
style={styles.floatingHeader}
|
||||||
@ -259,46 +295,21 @@ class StackViewLayout extends React.Component {
|
|||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const {
|
|
||||||
transitionProps: { navigation, scene, scenes },
|
|
||||||
} = this.props;
|
|
||||||
const { options } = scene.descriptor;
|
|
||||||
const { index } = navigation.state;
|
|
||||||
|
|
||||||
const gesturesEnabled =
|
|
||||||
typeof options.gesturesEnabled === 'boolean'
|
|
||||||
? options.gesturesEnabled
|
|
||||||
: Platform.OS === 'ios';
|
|
||||||
|
|
||||||
const containerStyle = [
|
|
||||||
styles.container,
|
|
||||||
this._getTransitionConfig().containerStyle,
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanGestureHandler
|
<PanGestureHandler
|
||||||
{...this._gestureActivationCriteria()}
|
{...this._gestureActivationCriteria()}
|
||||||
ref={this.panGestureRef}
|
ref={this.panGestureRef}
|
||||||
onGestureEvent={Animated.event(
|
onGestureEvent={this.gestureEvent}
|
||||||
[
|
|
||||||
{
|
|
||||||
nativeEvent: {
|
|
||||||
translationX: this.gestureX,
|
|
||||||
translationY: this.gestureY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
useNativeDriver: USE_NATIVE_DRIVER,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
onHandlerStateChange={this._handlePanGestureStateChange}
|
onHandlerStateChange={this._handlePanGestureStateChange}
|
||||||
enabled={index > 0 && gesturesEnabled}
|
enabled={index > 0 && this._isGesturesEnabled()}
|
||||||
>
|
>
|
||||||
<Animated.View style={containerStyle}>
|
<Animated.View
|
||||||
|
style={[styles.container, this._transitionConfig.containerStyle]}
|
||||||
|
>
|
||||||
<StackGestureContext.Provider value={this.panGestureRef}>
|
<StackGestureContext.Provider value={this.panGestureRef}>
|
||||||
<ScreenContainer style={styles.scenes}>
|
<ScreenContainer style={styles.scenes}>
|
||||||
{scenes.map(s => this._renderCard(s))}
|
{scenes.map(this._renderCard)}
|
||||||
</ScreenContainer>
|
</ScreenContainer>
|
||||||
{floatingHeader}
|
{floatingHeader}
|
||||||
</StackGestureContext.Provider>
|
</StackGestureContext.Provider>
|
||||||
@ -315,7 +326,7 @@ class StackViewLayout extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getGestureResponseDistance = () => {
|
_getGestureResponseDistance() {
|
||||||
const { scene } = this.props.transitionProps;
|
const { scene } = this.props.transitionProps;
|
||||||
const { options } = scene.descriptor;
|
const { options } = scene.descriptor;
|
||||||
const {
|
const {
|
||||||
@ -328,15 +339,15 @@ class StackViewLayout extends React.Component {
|
|||||||
GESTURE_RESPONSE_DISTANCE_VERTICAL
|
GESTURE_RESPONSE_DISTANCE_VERTICAL
|
||||||
: userGestureResponseDistance.horizontal ||
|
: userGestureResponseDistance.horizontal ||
|
||||||
GESTURE_RESPONSE_DISTANCE_HORIZONTAL;
|
GESTURE_RESPONSE_DISTANCE_HORIZONTAL;
|
||||||
};
|
}
|
||||||
|
|
||||||
_gestureActivationCriteria = () => {
|
_gestureActivationCriteria() {
|
||||||
let { layout } = this.props.transitionProps;
|
const { layout } = this.props.transitionProps;
|
||||||
let gestureResponseDistance = this._getGestureResponseDistance();
|
const gestureResponseDistance = this._getGestureResponseDistance();
|
||||||
let isMotionInverted = this._isMotionInverted();
|
const isMotionInverted = this._isMotionInverted();
|
||||||
|
|
||||||
if (this._isMotionVertical()) {
|
if (this._isMotionVertical()) {
|
||||||
let height = layout.height.__getValue();
|
const height = layout.height.__getValue();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
maxDeltaX: 15,
|
maxDeltaX: 15,
|
||||||
@ -346,8 +357,8 @@ class StackViewLayout extends React.Component {
|
|||||||
: { bottom: -height + gestureResponseDistance },
|
: { bottom: -height + gestureResponseDistance },
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
let width = layout.width.__getValue();
|
const width = layout.width.__getValue();
|
||||||
let hitSlop = -width + gestureResponseDistance;
|
const hitSlop = -width + gestureResponseDistance;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
minOffsetX: isMotionInverted ? -5 : 5,
|
minOffsetX: isMotionInverted ? -5 : 5,
|
||||||
@ -355,28 +366,26 @@ class StackViewLayout extends React.Component {
|
|||||||
hitSlop: isMotionInverted ? { left: hitSlop } : { right: hitSlop },
|
hitSlop: isMotionInverted ? { left: hitSlop } : { right: hitSlop },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Without using Reanimated it's not possible to do all of the following
|
_isGesturesEnabled() {
|
||||||
// stuff with native driver.
|
const gesturesEnabled = this.props.transitionProps.scene.descriptor.options
|
||||||
_handlePanGestureEvent = ({ nativeEvent }) => {
|
.gesturesEnabled;
|
||||||
if (this._isMotionVertical()) {
|
return typeof gesturesEnabled === 'boolean'
|
||||||
this._handleVerticalPan(nativeEvent);
|
? gesturesEnabled
|
||||||
} else {
|
: Platform.OS === 'ios';
|
||||||
this._handleHorizontalPan(nativeEvent);
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_isMotionVertical = () => {
|
_isMotionVertical() {
|
||||||
return this._isModal();
|
return this._isModal();
|
||||||
};
|
}
|
||||||
|
|
||||||
_isModal = () => {
|
_isModal() {
|
||||||
return this.props.mode === 'modal';
|
return this.props.mode === 'modal';
|
||||||
};
|
}
|
||||||
|
|
||||||
// This only currently applies to the horizontal gesture!
|
// This only currently applies to the horizontal gesture!
|
||||||
_isMotionInverted = () => {
|
_isMotionInverted() {
|
||||||
const {
|
const {
|
||||||
transitionProps: { scene },
|
transitionProps: { scene },
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -390,49 +399,44 @@ class StackViewLayout extends React.Component {
|
|||||||
? gestureDirection === 'inverted'
|
? gestureDirection === 'inverted'
|
||||||
: I18nManager.isRTL;
|
: I18nManager.isRTL;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
_handleHorizontalPan = nativeEvent => {
|
_computeHorizontalGestureValue({ translationX }) {
|
||||||
let value = this._computeHorizontalGestureValue(nativeEvent);
|
const {
|
||||||
this.props.transitionProps.position.setValue(Math.max(0, value));
|
|
||||||
};
|
|
||||||
|
|
||||||
_computeHorizontalGestureValue = ({ translationX }) => {
|
|
||||||
let {
|
|
||||||
transitionProps: { navigation, layout },
|
transitionProps: { navigation, layout },
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let { index } = navigation.state;
|
const { index } = navigation.state;
|
||||||
|
|
||||||
// TODO: remove this __getValue!
|
// TODO: remove this __getValue!
|
||||||
let distance = layout.width.__getValue();
|
const distance = layout.width.__getValue();
|
||||||
|
|
||||||
let x = this._isMotionInverted() ? -1 * translationX : translationX;
|
const x = this._isMotionInverted() ? -1 * translationX : translationX;
|
||||||
|
|
||||||
let value = index - x / distance;
|
const value = index - x / distance;
|
||||||
return clamp(index - 1, value, index);
|
return clamp(index - 1, value, index);
|
||||||
};
|
}
|
||||||
|
|
||||||
_computeVerticalGestureValue = ({ translationY }) => {
|
_computeVerticalGestureValue({ translationY }) {
|
||||||
let {
|
const {
|
||||||
transitionProps: { navigation, layout },
|
transitionProps: { navigation, layout },
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let { index } = navigation.state;
|
const { index } = navigation.state;
|
||||||
|
|
||||||
// TODO: remove this __getValue!
|
// TODO: remove this __getValue!
|
||||||
let distance = layout.height.__getValue();
|
const distance = layout.height.__getValue();
|
||||||
|
|
||||||
let y = this._isMotionInverted() ? -1 * translationY : translationY;
|
const y = this._isMotionInverted() ? -1 * translationY : translationY;
|
||||||
let value = index - y / distance;
|
const value = index - y / distance;
|
||||||
return clamp(index - 1, value, index);
|
return clamp(index - 1, value, index);
|
||||||
};
|
}
|
||||||
|
|
||||||
_handlePanGestureStateChange = ({ nativeEvent }) => {
|
_handlePanGestureStateChange = ({ nativeEvent }) => {
|
||||||
if (nativeEvent.oldState === State.ACTIVE) {
|
if (nativeEvent.oldState === State.ACTIVE) {
|
||||||
// Gesture was cancelled! For example, some navigation state update
|
// Gesture was cancelled! For example, some navigation state update
|
||||||
// arrived while the gesture was active that cancelled it out
|
// arrived while the gesture was active that cancelled it out
|
||||||
if (!this.state.gesturePosition) {
|
if (this.positionSwitch.__getValue() === 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,10 +446,9 @@ class StackViewLayout extends React.Component {
|
|||||||
this._handleReleaseHorizontal(nativeEvent);
|
this._handleReleaseHorizontal(nativeEvent);
|
||||||
}
|
}
|
||||||
} else if (nativeEvent.state === State.ACTIVE) {
|
} else if (nativeEvent.state === State.ACTIVE) {
|
||||||
if (this._isMotionVertical()) {
|
// HACK if current is in animation don't start gesture
|
||||||
this._handleActivateGestureVertical(nativeEvent);
|
if (!this.props.transitionProps.position._animation) {
|
||||||
} else {
|
this.positionSwitch.setValue(0);
|
||||||
this._handleActivateGestureHorizontal(nativeEvent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -454,87 +457,87 @@ class StackViewLayout extends React.Component {
|
|||||||
// of the gesturePosition, so if we are in the middle of swiping the screen away
|
// of the gesturePosition, so if we are in the middle of swiping the screen away
|
||||||
// and back is programatically fired then we will reset to the initial position
|
// and back is programatically fired then we will reset to the initial position
|
||||||
// and animate from there
|
// and animate from there
|
||||||
_maybeCancelGesture = () => {
|
_maybeCancelGesture() {
|
||||||
if (this.state.gesturePosition) {
|
this.positionSwitch.setValue(1);
|
||||||
this.setState({ gesturePosition: null });
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_handleActivateGestureHorizontal = () => {
|
_prepareGesture() {
|
||||||
let { index } = this.props.transitionProps.navigation.state;
|
if (!this._isGesturesEnabled()) {
|
||||||
|
if (this.positionSwitch.__getValue() !== 1) {
|
||||||
|
this.positionSwitch.setValue(1);
|
||||||
|
}
|
||||||
|
this.gesturePosition = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._isMotionVertical()) {
|
||||||
|
this._prepareGestureVertical();
|
||||||
|
} else {
|
||||||
|
this._prepareGestureHorizontal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_prepareGestureHorizontal() {
|
||||||
|
const { index } = this.props.transitionProps.navigation.state;
|
||||||
|
|
||||||
if (this._isMotionInverted()) {
|
if (this._isMotionInverted()) {
|
||||||
this.setState({
|
this.gesturePosition = Animated.add(
|
||||||
gesturePosition: Animated.add(
|
index,
|
||||||
index,
|
Animated.divide(this.gestureX, this.props.transitionProps.layout.width)
|
||||||
|
).interpolate({
|
||||||
|
inputRange: [index - 1, index],
|
||||||
|
outputRange: [index - 1, index],
|
||||||
|
extrapolate: 'clamp',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.gesturePosition = Animated.add(
|
||||||
|
index,
|
||||||
|
Animated.multiply(
|
||||||
|
-1,
|
||||||
Animated.divide(
|
Animated.divide(
|
||||||
this.gestureX,
|
this.gestureX,
|
||||||
this.props.transitionProps.layout.width
|
this.props.transitionProps.layout.width
|
||||||
)
|
)
|
||||||
).interpolate({
|
)
|
||||||
inputRange: [index - 1, index],
|
).interpolate({
|
||||||
outputRange: [index - 1, index],
|
inputRange: [index - 1, index],
|
||||||
extrapolate: 'clamp',
|
outputRange: [index - 1, index],
|
||||||
}),
|
extrapolate: 'clamp',
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
gesturePosition: Animated.add(
|
|
||||||
index,
|
|
||||||
Animated.multiply(
|
|
||||||
-1,
|
|
||||||
Animated.divide(
|
|
||||||
this.gestureX,
|
|
||||||
this.props.transitionProps.layout.width
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).interpolate({
|
|
||||||
inputRange: [index - 1, index],
|
|
||||||
outputRange: [index - 1, index],
|
|
||||||
extrapolate: 'clamp',
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
_handleActivateGestureVertical = () => {
|
_prepareGestureVertical() {
|
||||||
let { index } = this.props.transitionProps.navigation.state;
|
const { index } = this.props.transitionProps.navigation.state;
|
||||||
|
|
||||||
if (this._isMotionInverted()) {
|
if (this._isMotionInverted()) {
|
||||||
this.setState({
|
this.gesturePosition = Animated.add(
|
||||||
gesturePosition: Animated.add(
|
index,
|
||||||
index,
|
Animated.divide(this.gestureY, this.props.transitionProps.layout.height)
|
||||||
|
).interpolate({
|
||||||
|
inputRange: [index - 1, index],
|
||||||
|
outputRange: [index - 1, index],
|
||||||
|
extrapolate: 'clamp',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.gesturePosition = Animated.add(
|
||||||
|
index,
|
||||||
|
Animated.multiply(
|
||||||
|
-1,
|
||||||
Animated.divide(
|
Animated.divide(
|
||||||
this.gestureY,
|
this.gestureY,
|
||||||
this.props.transitionProps.layout.height
|
this.props.transitionProps.layout.height
|
||||||
)
|
)
|
||||||
).interpolate({
|
)
|
||||||
inputRange: [index - 1, index],
|
).interpolate({
|
||||||
outputRange: [index - 1, index],
|
inputRange: [index - 1, index],
|
||||||
extrapolate: 'clamp',
|
outputRange: [index - 1, index],
|
||||||
}),
|
extrapolate: 'clamp',
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
gesturePosition: Animated.add(
|
|
||||||
index,
|
|
||||||
Animated.multiply(
|
|
||||||
-1,
|
|
||||||
Animated.divide(
|
|
||||||
this.gestureY,
|
|
||||||
this.props.transitionProps.layout.height
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).interpolate({
|
|
||||||
inputRange: [index - 1, index],
|
|
||||||
outputRange: [index - 1, index],
|
|
||||||
extrapolate: 'clamp',
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
_handleReleaseHorizontal = nativeEvent => {
|
_handleReleaseHorizontal(nativeEvent) {
|
||||||
const {
|
const {
|
||||||
transitionProps: { navigation, position, layout },
|
transitionProps: { navigation, position, layout },
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -558,35 +561,35 @@ class StackViewLayout extends React.Component {
|
|||||||
|
|
||||||
// Get the current position value and reset to using the statically driven
|
// Get the current position value and reset to using the statically driven
|
||||||
// (rather than gesture driven) position.
|
// (rather than gesture driven) position.
|
||||||
let value = this._computeHorizontalGestureValue(nativeEvent);
|
const value = this._computeHorizontalGestureValue(nativeEvent);
|
||||||
position.setValue(value);
|
position.setValue(value);
|
||||||
this.setState({ gesturePosition: null }, () => {
|
this.positionSwitch.setValue(1);
|
||||||
// If the speed of the gesture release is significant, use that as the indication
|
|
||||||
// of intent
|
|
||||||
if (gestureVelocity < -50) {
|
|
||||||
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
|
||||||
this._reset(immediateIndex, resetDuration);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (gestureVelocity > 50) {
|
|
||||||
this.props.onGestureFinish && this.props.onGestureFinish();
|
|
||||||
this._goBack(immediateIndex, goBackDuration);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then filter based on the distance the screen was moved. Over a third of the way swiped,
|
// If the speed of the gesture release is significant, use that as the indication
|
||||||
// and the back will happen.
|
// of intent
|
||||||
if (value <= index - POSITION_THRESHOLD) {
|
if (gestureVelocity < -50) {
|
||||||
this.props.onGestureFinish && this.props.onGestureFinish();
|
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
||||||
this._goBack(immediateIndex, goBackDuration);
|
this._reset(immediateIndex, resetDuration);
|
||||||
} else {
|
return;
|
||||||
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
}
|
||||||
this._reset(immediateIndex, resetDuration);
|
if (gestureVelocity > 50) {
|
||||||
}
|
this.props.onGestureFinish && this.props.onGestureFinish();
|
||||||
});
|
this._goBack(immediateIndex, goBackDuration);
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_handleReleaseVertical = nativeEvent => {
|
// Then filter based on the distance the screen was moved. Over a third of the way swiped,
|
||||||
|
// and the back will happen.
|
||||||
|
if (value <= index - POSITION_THRESHOLD) {
|
||||||
|
this.props.onGestureFinish && this.props.onGestureFinish();
|
||||||
|
this._goBack(immediateIndex, goBackDuration);
|
||||||
|
} else {
|
||||||
|
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
||||||
|
this._reset(immediateIndex, resetDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleReleaseVertical(nativeEvent) {
|
||||||
const {
|
const {
|
||||||
transitionProps: { navigation, position, layout },
|
transitionProps: { navigation, position, layout },
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -609,33 +612,33 @@ class StackViewLayout extends React.Component {
|
|||||||
? movedDistance / velocity
|
? movedDistance / velocity
|
||||||
: (distance - movedDistance) / velocity;
|
: (distance - movedDistance) / velocity;
|
||||||
|
|
||||||
let value = this._computeVerticalGestureValue(nativeEvent);
|
const value = this._computeVerticalGestureValue(nativeEvent);
|
||||||
position.setValue(value);
|
position.setValue(value);
|
||||||
this.setState({ gesturePosition: null }, () => {
|
this.positionSwitch.setValue(1);
|
||||||
// If the speed of the gesture release is significant, use that as the indication
|
|
||||||
// of intent
|
|
||||||
if (gestureVelocity < -50) {
|
|
||||||
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
|
||||||
this._reset(immediateIndex, resetDuration);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (gestureVelocity > 50) {
|
|
||||||
this.props.onGestureFinish && this.props.onGestureFinish();
|
|
||||||
this._goBack(immediateIndex, goBackDuration);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then filter based on the distance the screen was moved. Over a third of the way swiped,
|
// If the speed of the gesture release is significant, use that as the indication
|
||||||
// and the back will happen.
|
// of intent
|
||||||
if (value <= index - POSITION_THRESHOLD) {
|
if (gestureVelocity < -50) {
|
||||||
this.props.onGestureFinish && this.props.onGestureFinish();
|
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
||||||
this._goBack(immediateIndex, goBackDuration);
|
this._reset(immediateIndex, resetDuration);
|
||||||
} else {
|
return;
|
||||||
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
}
|
||||||
this._reset(immediateIndex, resetDuration);
|
if (gestureVelocity > 50) {
|
||||||
}
|
this.props.onGestureFinish && this.props.onGestureFinish();
|
||||||
});
|
this._goBack(immediateIndex, goBackDuration);
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then filter based on the distance the screen was moved. Over a third of the way swiped,
|
||||||
|
// and the back will happen.
|
||||||
|
if (value <= index - POSITION_THRESHOLD) {
|
||||||
|
this.props.onGestureFinish && this.props.onGestureFinish();
|
||||||
|
this._goBack(immediateIndex, goBackDuration);
|
||||||
|
} else {
|
||||||
|
this.props.onGestureCanceled && this.props.onGestureCanceled();
|
||||||
|
this._reset(immediateIndex, resetDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_getHeaderMode() {
|
_getHeaderMode() {
|
||||||
if (this.props.headerMode) {
|
if (this.props.headerMode) {
|
||||||
@ -662,14 +665,12 @@ class StackViewLayout extends React.Component {
|
|||||||
} else if (headerBackgroundTransitionPreset === 'toggle') {
|
} else if (headerBackgroundTransitionPreset === 'toggle') {
|
||||||
return HeaderStyleInterpolator.forBackgroundWithInactiveHidden;
|
return HeaderStyleInterpolator.forBackgroundWithInactiveHidden;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (__DEV__) {
|
||||||
if (__DEV__) {
|
console.error(
|
||||||
console.error(
|
`Invalid configuration applied for headerBackgroundTransitionPreset - expected one of ${HEADER_BACKGROUND_TRANSITION_PRESET.join(
|
||||||
`Invalid configuration applied for headerBackgroundTransitionPreset - expected one of ${HEADER_BACKGROUND_TRANSITION_PRESET.join(
|
', '
|
||||||
', '
|
)} but received ${JSON.stringify(headerBackgroundTransitionPreset)}`
|
||||||
)} but received ${JSON.stringify(headerBackgroundTransitionPreset)}`
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -779,41 +780,49 @@ class StackViewLayout extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_getTransitionConfig = () => {
|
_prepareTransitionConfig() {
|
||||||
return TransitionConfigs.getTransitionConfig(
|
this._transitionConfig = TransitionConfigs.getTransitionConfig(
|
||||||
this.props.transitionConfig,
|
this.props.transitionConfig,
|
||||||
{
|
{
|
||||||
...this.props.transitionProps,
|
...this.props.transitionProps,
|
||||||
position: this._getPosition(),
|
position: this.position,
|
||||||
},
|
},
|
||||||
this.props.lastTransitionProps,
|
this.props.lastTransitionProps,
|
||||||
this._isModal()
|
this._isModal()
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
_getPosition = () => {
|
_preparePosition() {
|
||||||
if (!this.state.gesturePosition) {
|
if (this.gesturePosition) {
|
||||||
return this.props.transitionProps.position;
|
this.position = Animated.add(
|
||||||
} else {
|
Animated.multiply(
|
||||||
let { gesturePosition } = this.state;
|
this.props.transitionProps.position,
|
||||||
let staticPosition = Animated.add(
|
this.positionSwitch
|
||||||
this.props.transitionProps.position,
|
),
|
||||||
Animated.multiply(-1, this.props.transitionProps.position)
|
Animated.multiply(this.gesturePosition, this.gestureSwitch)
|
||||||
);
|
);
|
||||||
return Animated.add(gesturePosition, staticPosition);
|
} else {
|
||||||
|
this.position = this.props.transitionProps.position;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
_renderCard = scene => {
|
_renderCard = scene => {
|
||||||
const { screenInterpolator } = this._getTransitionConfig();
|
const {
|
||||||
|
transitionProps,
|
||||||
|
shadowEnabled,
|
||||||
|
cardOverlayEnabled,
|
||||||
|
transparentCard,
|
||||||
|
cardStyle,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const { screenInterpolator } = this._transitionConfig;
|
||||||
const style =
|
const style =
|
||||||
screenInterpolator &&
|
screenInterpolator &&
|
||||||
screenInterpolator({
|
screenInterpolator({
|
||||||
...this.props.transitionProps,
|
...transitionProps,
|
||||||
shadowEnabled: this.props.shadowEnabled,
|
shadowEnabled,
|
||||||
cardOverlayEnabled: this.props.cardOverlayEnabled,
|
cardOverlayEnabled,
|
||||||
position: this._getPosition(),
|
position: this.position,
|
||||||
scene,
|
scene,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -822,20 +831,20 @@ class StackViewLayout extends React.Component {
|
|||||||
const { options } = scene.descriptor;
|
const { options } = scene.descriptor;
|
||||||
const hasHeader = options.header !== null;
|
const hasHeader = options.header !== null;
|
||||||
const headerMode = this._getHeaderMode();
|
const headerMode = this._getHeaderMode();
|
||||||
let paddingTop = 0;
|
let paddingTopStyle;
|
||||||
if (hasHeader && headerMode === 'float' && !options.headerTransparent) {
|
if (hasHeader && headerMode === 'float' && !options.headerTransparent) {
|
||||||
paddingTop = this.state.floatingHeaderHeight;
|
paddingTopStyle = { paddingTop: this.state.floatingHeaderHeight };
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
{...this.props.transitionProps}
|
{...transitionProps}
|
||||||
key={`card_${scene.key}`}
|
key={`card_${scene.key}`}
|
||||||
position={this._getPosition()}
|
position={this.position}
|
||||||
realPosition={this.props.transitionProps.position}
|
realPosition={transitionProps.position}
|
||||||
animatedStyle={style}
|
animatedStyle={style}
|
||||||
transparent={this.props.transparentCard}
|
transparent={transparentCard}
|
||||||
style={[{ paddingTop }, this.props.cardStyle]}
|
style={[paddingTopStyle, cardStyle]}
|
||||||
scene={scene}
|
scene={scene}
|
||||||
>
|
>
|
||||||
{this._renderInnerScene(scene)}
|
{this._renderInnerScene(scene)}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user