Fix stack navigator animations (#1493) (#2520)

* Fix animation (#1493)

* Rewrite the file animatedInterpolate.js. To increase readability.

* Rename the variable, To increase readability.

* minor renaming
This commit is contained in:
94cstyles
2017-10-26 15:25:59 -05:00
parent 023b0990a9
commit 236e4b7f2d
4 changed files with 153 additions and 42 deletions

View File

@@ -493,3 +493,8 @@ export type LayoutEvent = {
},
},
};
export type SceneIndicesForInterpolationInputRange = {
first: number,
last: number,
};

View File

@@ -0,0 +1,56 @@
/* @flow */
import type {
NavigationSceneRendererProps,
NavigationScene,
SceneIndicesForInterpolationInputRange,
} from '../TypeDefinition';
function getSceneIndicesForInterpolationInputRange(
props: NavigationSceneRendererProps
): SceneIndicesForInterpolationInputRange | null {
const { scene, scenes } = props;
const index = scene.index;
const lastSceneIndexInScenes = scenes.length - 1;
const isBack = !scenes[lastSceneIndexInScenes].isActive;
if (isBack) {
const currentSceneIndexInScenes = scenes.findIndex(
(item: NavigationScene) => item === scene
);
const targetSceneIndexInScenes = scenes.findIndex(
(item: NavigationScene) => item.isActive
);
const targetSceneIndex = scenes[targetSceneIndexInScenes].index;
const lastSceneIndex = scenes[lastSceneIndexInScenes].index;
if (
index !== targetSceneIndex &&
currentSceneIndexInScenes === lastSceneIndexInScenes
) {
return {
first: Math.min(targetSceneIndex, index - 1),
last: index + 1,
};
} else if (
index === targetSceneIndex &&
currentSceneIndexInScenes === targetSceneIndexInScenes
) {
return {
first: index - 1,
last: Math.max(lastSceneIndex, index + 1),
};
} else if (
index === targetSceneIndex ||
currentSceneIndexInScenes > targetSceneIndexInScenes
) {
return null;
} else {
return { first: index - 1, last: index + 1 };
}
} else {
return { first: index - 1, last: index + 1 };
}
}
export default getSceneIndicesForInterpolationInputRange;

View File

@@ -7,6 +7,8 @@ import type {
AnimatedViewStyleProp,
} from '../../TypeDefinition';
import getSceneIndicesForInterpolationInputRange from '../../utils/getSceneIndicesForInterpolationInputRange';
/**
* Utility that builds the style for the card in the cards stack.
*
@@ -51,33 +53,25 @@ function forHorizontal(
if (!layout.isMeasured) {
return forInitial(props);
}
const interpolate = getSceneIndicesForInterpolationInputRange(props);
if (!interpolate) return { opacity: 0 };
const { first, last } = interpolate;
const index = scene.index;
const inputRange = [index - 1, index, index + 1];
const width = layout.initWidth;
const outputRange = I18nManager.isRTL
? ([-width, 0, width * 0.3]: Array<number>)
: ([width, 0, width * -0.3]: Array<number>);
// Add [index - 1, index - 0.99] to the interpolated opacity for screen transition.
// This makes the screen's shadow to disappear smoothly.
const opacity = position.interpolate({
inputRange: ([
index - 1,
index - 0.99,
index,
index + 0.99,
index + 1,
]: Array<number>),
inputRange: [first, first + 0.01, index, last - 0.01, last],
outputRange: ([0, 1, 1, 0.85, 0]: Array<number>),
});
const translateY = 0;
const width = layout.initWidth;
const translateX = position.interpolate({
inputRange,
outputRange,
inputRange: ([first, index, last]: Array<number>),
outputRange: I18nManager.isRTL
? ([-width, 0, width * 0.3]: Array<number>)
: ([width, 0, width * -0.3]: Array<number>),
});
const translateY = 0;
return {
opacity,
@@ -96,26 +90,23 @@ function forVertical(
if (!layout.isMeasured) {
return forInitial(props);
}
const interpolate = getSceneIndicesForInterpolationInputRange(props);
if (!interpolate) return { opacity: 0 };
const { first, last } = interpolate;
const index = scene.index;
const height = layout.initHeight;
const opacity = position.interpolate({
inputRange: ([
index - 1,
index - 0.99,
index,
index + 0.99,
index + 1,
]: Array<number>),
inputRange: [first, first + 0.01, index, last - 0.01, last],
outputRange: ([0, 1, 1, 0.85, 0]: Array<number>),
});
const translateX = 0;
const height = layout.initHeight;
const translateY = position.interpolate({
inputRange: ([index - 1, index, index + 1]: Array<number>),
inputRange: ([first, index, last]: Array<number>),
outputRange: ([height, 0, 0]: Array<number>),
});
const translateX = 0;
return {
opacity,
@@ -134,20 +125,24 @@ function forFadeFromBottomAndroid(
if (!layout.isMeasured) {
return forInitial(props);
}
const interpolate = getSceneIndicesForInterpolationInputRange(props);
if (!interpolate) return { opacity: 0 };
const { first, last } = interpolate;
const index = scene.index;
const inputRange = [index - 1, index, index + 0.99, index + 1];
const inputRange = ([first, index, last - 0.01, last]: Array<number>);
const opacity = position.interpolate({
inputRange,
outputRange: ([0, 1, 1, 0]: Array<number>),
});
const translateX = 0;
const translateY = position.interpolate({
inputRange,
outputRange: ([50, 0, 0, 0]: Array<number>),
});
const translateX = 0;
return {
opacity,
@@ -155,6 +150,31 @@ function forFadeFromBottomAndroid(
};
}
/**
* fadeIn and fadeOut
*/
function forFade(props: NavigationSceneRendererProps): AnimatedViewStyleProp {
const { layout, position, scene } = props;
if (!layout.isMeasured) {
return forInitial(props);
}
const interpolate = getSceneIndicesForInterpolationInputRange(props);
if (!interpolate) return { opacity: 0 };
const { first, last } = interpolate;
const index = scene.index;
const opacity = position.interpolate({
inputRange: ([first, index, last]: Array<number>),
outputRange: ([0, 1, 1]: Array<number>),
});
return {
opacity,
};
}
function canUseNativeDriver(): boolean {
// The native driver can be enabled for this interpolator animating
// opacity, translateX, and translateY is supported by the native animation
@@ -166,5 +186,6 @@ export default {
forHorizontal,
forVertical,
forFadeFromBottomAndroid,
forFade,
canUseNativeDriver,
};

View File

@@ -4,9 +4,12 @@ import { I18nManager } from 'react-native';
import type {
NavigationSceneRendererProps,
NavigationScene,
AnimatedViewStyleProp,
} from '../../TypeDefinition';
import getSceneIndicesForInterpolationInputRange from '../../utils/getSceneIndicesForInterpolationInputRange';
/**
* Utility that builds the style for the navigation header.
*
@@ -19,11 +22,25 @@ import type {
*/
function forLeft(props: NavigationSceneRendererProps): AnimatedViewStyleProp {
const { position, scene } = props;
const { index } = scene;
const { position, scene, scenes } = props;
const interpolate = getSceneIndicesForInterpolationInputRange(props);
if (!interpolate) return { opacity: 0 };
const activeScene = scenes.find((item: NavigationScene) => item.isActive);
const activeIndex = scenes.findIndex(
(item: NavigationScene) => item === activeScene
);
const currentIndex = scenes.findIndex(
(item: NavigationScene) => item === scene
);
const deviation = Math.abs((activeIndex - currentIndex) / 2);
const { first, last } = interpolate;
const index = scene.index;
return {
opacity: position.interpolate({
inputRange: [index - 1, index - 0.5, index, index + 0.5, index + 1],
inputRange: [first, first + deviation, index, last - deviation, last],
outputRange: ([0, 0, 1, 0, 0]: Array<number>),
}),
};
@@ -31,19 +48,26 @@ function forLeft(props: NavigationSceneRendererProps): AnimatedViewStyleProp {
function forCenter(props: NavigationSceneRendererProps): AnimatedViewStyleProp {
const { position, scene } = props;
const { index } = scene;
const interpolate = getSceneIndicesForInterpolationInputRange(props);
if (!interpolate) return { opacity: 0 };
const { first, last } = interpolate;
const index = scene.index;
const inputRange = [first, index, last];
return {
opacity: position.interpolate({
inputRange: [index - 1, index, index + 1],
inputRange,
outputRange: ([0, 1, 0]: Array<number>),
}),
transform: [
{
translateX: position.interpolate({
inputRange: [index - 1, index + 1],
inputRange,
outputRange: I18nManager.isRTL
? ([-200, 200]: Array<number>)
: ([200, -200]: Array<number>),
? ([-200, 0, 200]: Array<number>)
: ([200, 0, -200]: Array<number>),
}),
},
],
@@ -52,10 +76,15 @@ function forCenter(props: NavigationSceneRendererProps): AnimatedViewStyleProp {
function forRight(props: NavigationSceneRendererProps): AnimatedViewStyleProp {
const { position, scene } = props;
const { index } = scene;
const interpolate = getSceneIndicesForInterpolationInputRange(props);
if (!interpolate) return { opacity: 0 };
const { first, last } = interpolate;
const index = scene.index;
return {
opacity: position.interpolate({
inputRange: [index - 1, index, index + 1],
inputRange: [first, index, last],
outputRange: ([0, 1, 0]: Array<number>),
}),
};