react-native/Libraries/NavigationExperimental/NavigationLinearPanResponde...

184 lines
4.4 KiB
JavaScript

/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule NavigationLinearPanResponder
* @flow
* @typechecks
*/
'use strict';
const Animated = require('Animated');
const NavigationAbstractPanResponder = require('NavigationAbstractPanResponder');
const clamp = require('clamp');
/**
* The duration of the card animation in milliseconds.
*/
const ANIMATION_DURATION = 250;
/**
* The threshold to invoke the `onNavigate` action.
* For instance, `1 / 3` means that moving greater than 1 / 3 of the width of
* the view will navigate.
*/
const POSITION_THRESHOLD = 1 / 3;
/**
* The threshold (in pixels) to start the gesture action.
*/
const RESPOND_THRESHOLD = 15;
/**
* The threshold (in speed) to finish the gesture action.
*/
const VELOCITY_THRESHOLD = 100;
/**
* Primitive gesture directions.
*/
const Directions = {
'HORIZONTAL': 'horizontal',
'VERTICAL': 'vertical',
};
/**
* Primitive gesture actions.
*/
const Actions = {
// The gesture to navigate backward.
// This is done by swiping from the left to the right or from the top to the
// bottom.
BACK: {type: 'back'},
};
import type {
Layout,
Position,
} from 'NavigationAnimatedView';
export type OnNavigateHandler = (action: {type: string}) => void;
export type Direction = $Enum<typeof Directions>;
/**
* The type interface of the object that provides the information required by
* NavigationLinearPanResponder.
*/
export type NavigationLinearPanResponderDelegate = {
getDirection: () => Direction;
getIndex: () => number,
getLayout: () => Layout,
getPosition: () => Position,
onNavigate: OnNavigateHandler,
};
/**
* Pan responder that handles the One-dimensional gesture (horizontal or
* vertical).
*/
class NavigationLinearPanResponder extends NavigationAbstractPanResponder {
static Actions: Object;
static Directions: Object;
_isResponding: boolean;
_startValue: number;
_delegate: NavigationLinearPanResponderDelegate;
constructor(delegate: NavigationLinearPanResponderDelegate) {
super();
this._isResponding = false;
this._startValue = 0;
this._delegate = delegate;
}
onMoveShouldSetPanResponder(event: any, gesture: any): boolean {
const delegate = this._delegate;
const layout = delegate.getLayout();
const isVertical = delegate.getDirection() === Directions.VERTICAL;
const axis = isVertical ? 'dy' : 'dx';
const index = delegate.getIndex();
const distance = isVertical ?
layout.height.__getValue() :
layout.width.__getValue();
return (
Math.abs(gesture[axis]) > RESPOND_THRESHOLD &&
distance > 0 &&
index > 0
);
}
onPanResponderGrant(): void {
this._isResponding = false;
this._delegate.getPosition().stopAnimation((value: number) => {
this._isResponding = true;
this._startValue = value;
});
}
onPanResponderMove(event: any, gesture: any): void {
if (!this._isResponding) {
return;
}
const delegate = this._delegate;
const layout = delegate.getLayout();
const isVertical = delegate.getDirection() === Directions.VERTICAL;
const axis = isVertical ? 'dy' : 'dx';
const index = delegate.getIndex();
const distance = isVertical ?
layout.height.__getValue() :
layout.width.__getValue();
const value = clamp(
index - 1,
this._startValue - (gesture[axis] / distance),
index
);
this._delegate.getPosition().setValue(value);
}
onPanResponderRelease(event: any, gesture: any): void {
if (!this._isResponding) {
return;
}
this._isResponding = false;
const delegate = this._delegate;
const isVertical = delegate.getDirection() === Directions.VERTICAL;
const axis = isVertical ? 'dy' : 'dx';
const index = delegate.getIndex();
const velocity = gesture[axis];
delegate.getPosition().stopAnimation((value: number) => {
this._reset();
if (velocity > VELOCITY_THRESHOLD || value <= index - POSITION_THRESHOLD) {
delegate.onNavigate(Actions.BACK);
}
});
}
onPanResponderTerminate(): void {
this._isResponding = false;
this._reset();
}
_reset(): void {
Animated.timing(
this._delegate.getPosition(),
{
toValue: this._delegate.getIndex(),
duration: ANIMATION_DURATION,
}
).start();
}
}
NavigationLinearPanResponder.Actions = Actions;
NavigationLinearPanResponder.Directions = Directions;
module.exports = NavigationLinearPanResponder;