2
0
mirror of https://github.com/status-im/react-navigation-stack.git synced 2025-02-22 03:08:05 +00:00

Full gesture flow on horizontal

This commit is contained in:
Brent Vatne 2018-09-24 16:15:21 -07:00
parent 23ea16e9cf
commit b8cee7ac7f

@ -19,10 +19,7 @@ import {
NavigationProvider, NavigationProvider,
} from 'react-navigation'; } from 'react-navigation';
import { ScreenContainer } from 'react-native-screens'; import { ScreenContainer } from 'react-native-screens';
import { import { PanGestureHandler, State } from 'react-native-gesture-handler';
PanGestureHandler,
NativeViewGestureHandler,
} from 'react-native-gesture-handler';
import Card from './StackViewCard'; import Card from './StackViewCard';
import Header from '../Header/Header'; import Header from '../Header/Header';
@ -277,9 +274,10 @@ class StackViewLayout extends React.Component {
); );
} }
const { const {
transitionProps: { scene, scenes }, transitionProps: { navigation, scene, scenes },
} = this.props; } = this.props;
const { options } = scene.descriptor; const { options } = scene.descriptor;
const { index } = navigation.state;
const gesturesEnabled = const gesturesEnabled =
typeof options.gesturesEnabled === 'boolean' typeof options.gesturesEnabled === 'boolean'
@ -296,7 +294,7 @@ class StackViewLayout extends React.Component {
// https://github.com/kmagiera/react-native-gesture-handler/issues/293 // https://github.com/kmagiera/react-native-gesture-handler/issues/293
return ( return (
<PanGestureHandler <PanGestureHandler
minOffsetX={this._isGestureInverted() ? -15 : 15} minOffsetX={this._isMotionInverted() ? -15 : 15}
maxDeltaY={5} maxDeltaY={5}
onGestureEvent={this._handlePanGestureEvent} onGestureEvent={this._handlePanGestureEvent}
onHandlerStateChange={this._handlePanGestureStateChange} onHandlerStateChange={this._handlePanGestureStateChange}
@ -315,17 +313,19 @@ class StackViewLayout extends React.Component {
// Without using Reanimated it's not possible to do all of the following // Without using Reanimated it's not possible to do all of the following
// stuff with native driver. // stuff with native driver.
_handlePanGestureEvent = ({ nativeEvent }) => { _handlePanGestureEvent = ({ nativeEvent }) => {
const { mode } = this.props; if (this._isMotionVertical()) {
const isVertical = mode === 'modal';
if (isVertical) {
this._handleVerticalPan(nativeEvent); this._handleVerticalPan(nativeEvent);
} else { } else {
this._handleHorizontalPan(nativeEvent); this._handleHorizontalPan(nativeEvent);
} }
}; };
_isGestureInverted = () => { _isMotionVertical = () => {
const { mode } = this.props;
return mode === 'modal';
};
_isMotionInverted = () => {
const { const {
transitionProps: { scene }, transitionProps: { scene },
} = this.props; } = this.props;
@ -338,22 +338,25 @@ class StackViewLayout extends React.Component {
}; };
_handleHorizontalPan = nativeEvent => { _handleHorizontalPan = nativeEvent => {
let value = this._computeHorizontalGestureValue(nativeEvent);
console.log({value})
this.props.transitionProps.position.setValue(value);
};
_computeHorizontalGestureValue = nativeEvent => {
let { let {
transitionProps: { navigation, position, layout }, transitionProps: { navigation, layout },
} = this.props; } = this.props;
let { index } = navigation.state; let { index } = navigation.state;
let distance = layout.width.__getValue(); let distance = layout.width.__getValue();
let translation = nativeEvent.translationX; let translationX = this._isMotionInverted()
? -1 * nativeEvent.translationX
: nativeEvent.translationX;
if (this._isGestureInverted()) { let value = index - translationX / distance;
translation *= -1; console.log({ value, index, translationX })
} return clamp(index - 1, value, index);
let currentValue = 1 - translation / distance;
let value = clamp(index - 1, currentValue, index);
position.setValue(value);
}; };
_handleVerticalPan = nativeEvent => { _handleVerticalPan = nativeEvent => {
@ -361,8 +364,68 @@ class StackViewLayout extends React.Component {
}; };
_handlePanGestureStateChange = ({ nativeEvent }) => { _handlePanGestureStateChange = ({ nativeEvent }) => {
const { oldState, state } = nativeEvent; if (nativeEvent.oldState !== State.ACTIVE) {
// console.log({ nativeEvent }) return;
}
if (this._isMotionVertical()) {
this._handleReleaseVertical(nativeEvent);
} else {
this._handleReleaseHorizontal(nativeEvent);
}
};
_handleReleaseHorizontal = nativeEvent => {
const {
transitionProps: { navigation, position, layout, scene },
mode,
} = this.props;
const { index } = navigation.state;
const immediateIndex =
this._immediateIndex == null ? index : this._immediateIndex;
// Calculate animate duration according to gesture speed and moved distance
const distance = layout.width.__getValue();
const movementDirection = this._isMotionInverted() ? -1 : 1;
const movedDistance = movementDirection * nativeEvent.translationX;
const gestureVelocity = movementDirection * nativeEvent.velocityX;
const defaultVelocity = distance / ANIMATION_DURATION;
const velocity = Math.max(Math.abs(gestureVelocity), defaultVelocity);
const resetDuration = this._isMotionInverted()
? (distance - movedDistance) / velocity
: movedDistance / velocity;
const goBackDuration = this._isMotionInverted()
? movedDistance / velocity
: (distance - movedDistance) / velocity;
let value = this._computeHorizontalGestureValue(nativeEvent);
// If the speed of the gesture release is significant, use that as the indication
// of intent
if (gestureVelocity < -0.5) {
this.props.onGestureCanceled && this.props.onGestureCanceled();
this._reset(immediateIndex, resetDuration);
return;
}
if (gestureVelocity > 0.5) {
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);
}
};
_handleReleaseVertical = nativeEvent => {
// todo
}; };
_getHeaderMode() { _getHeaderMode() {