mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-11 09:20:54 +08:00
* Fix animation (#1493) * Rewrite the file animatedInterpolate.js. To increase readability. * Rename the variable, To increase readability. * minor renaming
This commit is contained in:
@@ -493,3 +493,8 @@ export type LayoutEvent = {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export type SceneIndicesForInterpolationInputRange = {
|
||||
first: number,
|
||||
last: number,
|
||||
};
|
||||
|
||||
56
packages/react-navigation/src/utils/getSceneIndicesForInterpolationInputRange.js
vendored
Normal file
56
packages/react-navigation/src/utils/getSceneIndicesForInterpolationInputRange.js
vendored
Normal 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;
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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>),
|
||||
}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user