mirror of
https://github.com/status-im/react-navigation.git
synced 2025-02-24 17:18:09 +00:00
parent
2273ef1c14
commit
6aff0ac366
@ -31,12 +31,21 @@ const POSITION_THRESHOLD = 1 / 3;
|
||||
/**
|
||||
* The threshold (in pixels) to start the gesture action.
|
||||
*/
|
||||
const RESPOND_THRESHOLD = 15;
|
||||
const RESPOND_THRESHOLD = 12;
|
||||
|
||||
/**
|
||||
* The threshold (in pixels) to finish the gesture action.
|
||||
* The distance of touch start from the edge of the screen where the gesture will be recognized
|
||||
*/
|
||||
const DISTANCE_THRESHOLD = 100;
|
||||
const GESTURE_RESPONSE_DISTANCE = 35;
|
||||
|
||||
/**
|
||||
* The ratio between the gesture velocity and the animation velocity. This allows
|
||||
* the velocity of a swipe release to carry on into the new animation.
|
||||
*
|
||||
* TODO: Understand and compute this ratio rather than using an approximation
|
||||
*/
|
||||
const GESTURE_ANIMATED_VELOCITY_RATIO = -4;
|
||||
|
||||
|
||||
/**
|
||||
* Primitive gesture directions.
|
||||
@ -121,7 +130,7 @@ class CardStackPanResponder extends AbstractPanResponder {
|
||||
* For horizontal scroll views, a distance of 30 from the left of the screen is the
|
||||
* standard maximum position to start touch responsiveness.
|
||||
*/
|
||||
props.gestureResponseDistance || 30;
|
||||
props.gestureResponseDistance || GESTURE_RESPONSE_DISTANCE;
|
||||
|
||||
if (positionMax != null && (currentDragPosition - currentDragDistance) > positionMax) {
|
||||
return false;
|
||||
@ -178,30 +187,42 @@ class CardStackPanResponder extends AbstractPanResponder {
|
||||
const props = this._props;
|
||||
const isVertical = this._isVertical;
|
||||
const axis = isVertical ? 'dy' : 'dx';
|
||||
const velocity = gesture[isVertical ? 'vy' : 'vx'];
|
||||
const index = props.navigationState.index;
|
||||
const distance = I18nManager.isRTL && axis === 'dx' ?
|
||||
-gesture[axis] :
|
||||
gesture[axis];
|
||||
|
||||
// To asyncronously get the current animated value, we need to run stopAnimation:
|
||||
props.position.stopAnimation((value: number) => {
|
||||
this._reset();
|
||||
|
||||
if (!props.onNavigateBack) {
|
||||
this._reset(velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the speed of the gesture release is significant, use that as the indication
|
||||
// of intent
|
||||
if (velocity < -0.5) {
|
||||
this._reset(velocity);
|
||||
return;
|
||||
}
|
||||
if (velocity > 0.5) {
|
||||
this._goBack(velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
// Then filter based on the distance the screen was moved. Over a third of the way swiped,
|
||||
// and the back will happen.
|
||||
if (
|
||||
distance > DISTANCE_THRESHOLD ||
|
||||
value <= index - POSITION_THRESHOLD
|
||||
) {
|
||||
props.onNavigateBack();
|
||||
this._goBack(velocity);
|
||||
} else {
|
||||
this._reset(velocity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onPanResponderTerminate(): void {
|
||||
this._isResponding = false;
|
||||
this._reset();
|
||||
this._reset(0);
|
||||
}
|
||||
|
||||
onPanResponderTerminationRequest(event: any, gesture: any): boolean {
|
||||
@ -210,19 +231,38 @@ class CardStackPanResponder extends AbstractPanResponder {
|
||||
return false;
|
||||
}
|
||||
|
||||
_reset(): void {
|
||||
_reset(velocity: number): void {
|
||||
const props = this._props;
|
||||
Animated.timing(
|
||||
Animated.spring(
|
||||
props.position,
|
||||
{
|
||||
toValue: props.navigationState.index,
|
||||
duration: ANIMATION_DURATION,
|
||||
useNativeDriver: props.position.__isNative,
|
||||
velocity: velocity * GESTURE_ANIMATED_VELOCITY_RATIO,
|
||||
bounciness: 0,
|
||||
}
|
||||
).start();
|
||||
}
|
||||
|
||||
_addNativeListener(animatedValue) {
|
||||
_goBack(velocity: number) {
|
||||
const props = this._props;
|
||||
if (!props.onNavigateBack) {
|
||||
return;
|
||||
}
|
||||
Animated.spring(
|
||||
props.position,
|
||||
{
|
||||
toValue: Math.max(props.navigationState.index - 1, 0),
|
||||
duration: ANIMATION_DURATION,
|
||||
useNativeDriver: props.position.__isNative,
|
||||
velocity: velocity * GESTURE_ANIMATED_VELOCITY_RATIO,
|
||||
bounciness: 0,
|
||||
}
|
||||
).start(props.onNavigateBack);
|
||||
}
|
||||
|
||||
_addNativeListener(animatedValue: Animated.Value) {
|
||||
if (!animatedValue.__isNative) {
|
||||
return;
|
||||
}
|
||||
@ -256,8 +296,6 @@ function forVertical(
|
||||
export default {
|
||||
// constants
|
||||
ANIMATION_DURATION,
|
||||
DISTANCE_THRESHOLD,
|
||||
POSITION_THRESHOLD,
|
||||
RESPOND_THRESHOLD,
|
||||
|
||||
// enums
|
||||
|
@ -61,7 +61,10 @@ function forHorizontal(props: NavigationSceneRendererProps): Object {
|
||||
|
||||
const index = scene.index;
|
||||
const inputRange = [index - 1, index, index + 0.99, index + 1];
|
||||
const width = layout.initWidth;
|
||||
|
||||
// Add ~30px to the interpolated width screens width for horizontal movement. This allows
|
||||
// the screen's shadow to go screen fully offscreen without abruptly dissapearing
|
||||
const width = layout.initWidth + 30;
|
||||
const outputRange = I18nManager.isRTL ?
|
||||
([-width, 0, 10, 10]: Array<number>) :
|
||||
([width, 0, -10, -10]: Array<number>);
|
||||
|
@ -28,9 +28,11 @@ export type TransitionConfig = {
|
||||
|
||||
// Used for all animations unless overriden
|
||||
const DefaultTransitionSpec = ({
|
||||
// The following options are meant to mimic the nav animations of iOS 10
|
||||
duration: 250,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
timing: Animated.timing,
|
||||
timing: Animated.spring,
|
||||
bounciness: 0,
|
||||
speed: 9,
|
||||
} : NavigationTransitionSpec);
|
||||
|
||||
// Standard iOS navigation transition
|
||||
|
Loading…
x
Reference in New Issue
Block a user