stop using Animated.spring (#1500)

* stop using Animated.spring

* use layout instead of window.size
This commit is contained in:
Neo
2017-05-14 23:40:02 +08:00
parent c1a8e8e3ec
commit e957187126
2 changed files with 39 additions and 40 deletions

View File

@@ -10,6 +10,7 @@ import {
Platform,
View,
I18nManager,
Easing,
} from 'react-native';
import Card from './Card';
@@ -63,9 +64,11 @@ type Props = {
};
/**
* The duration of the card animation in milliseconds.
* The max duration of the card animation in milliseconds after released gesture.
* The actual duration should be always less then that because the rest distance
* is always less then the full distance of the layout.
*/
const ANIMATION_DURATION = 200;
const ANIMATION_DURATION = 250;
/**
* The gesture distance threshold to trigger the back behavior. For instance,
@@ -94,14 +97,6 @@ const animatedSubscribeValue = (animatedValue: Animated.Value) => {
}
};
/**
* 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;
class CardStack extends Component {
/**
* Used to identify the starting point of the position when the gesture starts, such that it can
@@ -199,17 +194,16 @@ class CardStack extends Component {
animatedSubscribeValue(props.position);
}
_reset(resetToIndex: number, velocity: number): void {
Animated.spring(this.props.position, {
_reset(resetToIndex: number, duration: number): void {
Animated.timing(this.props.position, {
toValue: resetToIndex,
duration: ANIMATION_DURATION,
duration,
easing: Easing.inOut(Easing.ease),
useNativeDriver: this.props.position.__isNative,
velocity: velocity * GESTURE_ANIMATED_VELOCITY_RATIO,
bounciness: 0,
}).start();
}
_goBack(backFromIndex: number, velocity: number) {
_goBack(backFromIndex: number, duration: number) {
const { navigation, position, scenes } = this.props;
const toValue = Math.max(backFromIndex - 1, 0);
@@ -217,12 +211,11 @@ class CardStack extends Component {
// dispatched at the end of the transition.
this._immediateIndex = toValue;
Animated.spring(position, {
Animated.timing(position, {
toValue,
duration: ANIMATION_DURATION,
duration,
easing: Easing.inOut(Easing.ease),
useNativeDriver: position.__isNative,
velocity: velocity * GESTURE_ANIMATED_VELOCITY_RATIO,
bounciness: 0,
}).start(() => {
this._immediateIndex = null;
const backFromScene = scenes.find((s: *) => s.index === toValue + 1);
@@ -240,8 +233,10 @@ class CardStack extends Component {
if (headerMode === 'float') {
floatingHeader = this._renderHeader(this.props.scene, headerMode);
}
const { navigation, position, scene, mode, scenes } = this.props;
const { navigation, position, layout, scene, scenes, mode } = this.props;
const { index } = navigation.state;
const isVertical = mode === 'modal';
const responder = PanResponder.create({
onPanResponderTerminate: () => {
this._isResponding = false;
@@ -257,11 +252,9 @@ class CardStack extends Component {
event: { nativeEvent: { pageY: number, pageX: number } },
gesture: any,
) => {
const layout = this.props.layout;
if (index !== scene.index) {
return false;
}
const isVertical = mode === 'modal';
const immediateIndex = this._immediateIndex == null
? index
: this._immediateIndex;
@@ -297,16 +290,14 @@ class CardStack extends Component {
},
onPanResponderMove: (event: any, gesture: any) => {
// Handle the moving touches for our granted responder
const layout = this.props.layout;
const isVertical = mode === 'modal';
const startValue = this._gestureStartValue;
const axis = isVertical ? 'dy' : 'dx';
const distance = isVertical
const axisDistance = isVertical
? layout.height.__getValue()
: layout.width.__getValue();
const currentValue = I18nManager.isRTL && axis === 'dx'
? startValue + gesture[axis] / distance
: startValue - gesture[axis] / distance;
? startValue + gesture[axis] / axisDistance
: startValue - gesture[axis] / axisDistance;
const value = clamp(index - 1, currentValue, index);
position.setValue(value);
},
@@ -319,31 +310,41 @@ class CardStack extends Component {
return;
}
this._isResponding = false;
const isVertical = mode === 'modal';
const velocity = gesture[isVertical ? 'vy' : 'vx'];
const immediateIndex = this._immediateIndex == null
? index
: this._immediateIndex;
// Calculate animate duration according to gesture speed and moved distance
const axisDistance = isVertical
? layout.height.__getValue()
: layout.width.__getValue();
const movedDistance = gesture[isVertical ? 'moveY' : 'moveX'];
const defaultVelocity = axisDistance / ANIMATION_DURATION;
const gestureVelocity = gesture[isVertical ? 'vy' : 'vx'];
const velocity = Math.max(gestureVelocity, defaultVelocity);
const resetDuration = movedDistance / velocity;
const goBackDuration = (axisDistance - movedDistance) / velocity;
// To asyncronously get the current animated value, we need to run stopAnimation:
position.stopAnimation((value: number) => {
// If the speed of the gesture release is significant, use that as the indication
// of intent
if (velocity < -0.5) {
this._reset(immediateIndex, velocity);
if (gestureVelocity < -0.5) {
this._reset(immediateIndex, resetDuration);
return;
}
if (velocity > 0.5) {
this._goBack(immediateIndex, velocity);
if (gestureVelocity > 0.5) {
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._goBack(immediateIndex, velocity);
this._goBack(immediateIndex, goBackDuration);
} else {
this._reset(immediateIndex, velocity);
this._reset(immediateIndex, resetDuration);
}
});
},

View File

@@ -12,11 +12,9 @@ import CardStackStyleInterpolator from './CardStackStyleInterpolator';
// Used for all animations unless overriden
const DefaultTransitionSpec = ({
// The following options are meant to mimic the nav animations of iOS 10
duration: 250,
timing: Animated.spring,
bounciness: 0,
speed: 9,
easing: Easing.inOut(Easing.ease),
timing: Animated.timing,
}: NavigationTransitionSpec);
// Standard iOS navigation transition