mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-24 04:16:00 +08:00
Expose a new prop transition for scene renderer.
Summary:
= Breaking Change (for experimental features) =
Major API changes in NavigationAnimatedView
= New prop `transition` for scene renderer
In NavigationAnimatedView, we should not use `position` as a proxy of the
transtion which happens whenever navigation state changes.
Because `position` does not change unless navigation index changes, it won't
be possible to build animations for actions that replace navigation state
without changing the index.
This diff introduces an abstract prop `transition` that is exposed to the scene
renderers.
= Replace `applyAnimation` with `configureTransition`.
Expose a new optional prop `configureTransition` that allows people to configure
transitions easily.
For instance, to configure the transition, do this:
```
function configureTransition() {
return {
dutation: 123,
easing: Easing.easeInOut,
};
}
```
<NavigationAnimatedView configureTransition={configureTransition) />
```
Reviewed By: ericvicenti
Differential Revision: D3278698
fbshipit-source-id: 25ebad286d8b41f46c35c0f32d6023ebd01f19e7
This commit is contained in:
committed by
Facebook Github Bot 0
parent
763e9cce27
commit
55c308615a
@@ -12,6 +12,7 @@
|
||||
'use strict';
|
||||
|
||||
const Animated = require('Animated');
|
||||
const Easing = require('Easing');
|
||||
const NavigationPropTypes = require('NavigationPropTypes');
|
||||
const NavigationScenesReducer = require('NavigationScenesReducer');
|
||||
const React = require('React');
|
||||
@@ -26,6 +27,7 @@ import type {
|
||||
NavigationParentState,
|
||||
NavigationScene,
|
||||
NavigationSceneRenderer,
|
||||
NavigationTransitionConfigurator,
|
||||
} from 'NavigationTypeDefinition';
|
||||
|
||||
type Props = {
|
||||
@@ -34,6 +36,7 @@ type Props = {
|
||||
onNavigate: NavigationActionCaller,
|
||||
renderOverlay: ?NavigationSceneRenderer,
|
||||
renderScene: NavigationSceneRenderer,
|
||||
configureTransition: NavigationTransitionConfigurator,
|
||||
style: any,
|
||||
};
|
||||
|
||||
@@ -41,10 +44,20 @@ type State = {
|
||||
layout: NavigationLayout,
|
||||
position: NavigationAnimatedValue,
|
||||
scenes: Array<NavigationScene>,
|
||||
transition: NavigationAnimatedValue,
|
||||
};
|
||||
|
||||
const {PropTypes} = React;
|
||||
|
||||
const DefaultTransitionSpec = {
|
||||
duration: 250,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
};
|
||||
|
||||
function isSceneNotStale(scene: NavigationScene): boolean {
|
||||
return !scene.isStale;
|
||||
}
|
||||
|
||||
function applyDefaultAnimation(
|
||||
position: NavigationAnimatedValue,
|
||||
navigationState: NavigationParentState,
|
||||
@@ -62,14 +75,14 @@ class NavigationAnimatedView
|
||||
extends React.Component<any, Props, State> {
|
||||
|
||||
_onLayout: (event: any) => void;
|
||||
_onProgressChange: (data: {value: number}) => void;
|
||||
_positionListener: any;
|
||||
_onTransitionEnd: () => void;
|
||||
|
||||
props: Props;
|
||||
state: State;
|
||||
|
||||
static propTypes = {
|
||||
applyAnimation: PropTypes.func,
|
||||
configureTransition: PropTypes.func,
|
||||
navigationState: NavigationPropTypes.navigationState.isRequired,
|
||||
onNavigate: PropTypes.func.isRequired,
|
||||
renderOverlay: PropTypes.func,
|
||||
@@ -78,6 +91,7 @@ class NavigationAnimatedView
|
||||
|
||||
static defaultProps = {
|
||||
applyAnimation: applyDefaultAnimation,
|
||||
configureTransition: () => DefaultTransitionSpec,
|
||||
};
|
||||
|
||||
constructor(props: Props, context: any) {
|
||||
@@ -97,58 +111,64 @@ class NavigationAnimatedView
|
||||
layout,
|
||||
position: new Animated.Value(this.props.navigationState.index),
|
||||
scenes: NavigationScenesReducer([], this.props.navigationState),
|
||||
transition: new Animated.Value(1),
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount(): void {
|
||||
this._onLayout = this._onLayout.bind(this);
|
||||
this._onProgressChange = this._onProgressChange.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._positionListener =
|
||||
this.state.position.addListener(this._onProgressChange);
|
||||
this._onTransitionEnd = this._onTransitionEnd.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Props): void {
|
||||
if (nextProps.navigationState !== this.props.navigationState) {
|
||||
this.setState({
|
||||
scenes: NavigationScenesReducer(
|
||||
this.state.scenes,
|
||||
nextProps.navigationState,
|
||||
this.props.navigationState
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
const nextScenes = NavigationScenesReducer(
|
||||
this.state.scenes,
|
||||
nextProps.navigationState,
|
||||
this.props.navigationState
|
||||
);
|
||||
|
||||
componentDidUpdate(lastProps: Props): void {
|
||||
if (lastProps.navigationState.index !== this.props.navigationState.index) {
|
||||
this.props.applyAnimation(
|
||||
this.state.position,
|
||||
this.props.navigationState,
|
||||
lastProps.navigationState
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.state.position.removeListener(this._positionListener);
|
||||
}
|
||||
|
||||
_onProgressChange(data: Object): void {
|
||||
const delta = Math.abs(data.value - this.props.navigationState.index);
|
||||
if (delta > Number.EPSILON) {
|
||||
if (nextScenes === this.state.scenes) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scenes = this.state.scenes.filter(scene => {
|
||||
return !scene.isStale;
|
||||
const {
|
||||
position,
|
||||
transition,
|
||||
} = this.state;
|
||||
|
||||
// update scenes.
|
||||
this.setState({
|
||||
scenes: nextScenes,
|
||||
});
|
||||
|
||||
if (scenes.length !== this.state.scenes.length) {
|
||||
this.setState({ scenes });
|
||||
// get the transition spec.
|
||||
const transtionSpec = nextProps.configureTransition();
|
||||
transition.setValue(0);
|
||||
|
||||
const animations = [
|
||||
Animated.timing(
|
||||
transition,
|
||||
{
|
||||
...transtionSpec,
|
||||
toValue: 1,
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
if (nextProps.navigationState.index !== this.props.navigationState.index) {
|
||||
animations.push(
|
||||
Animated.timing(
|
||||
position,
|
||||
{
|
||||
...transtionSpec,
|
||||
toValue: nextProps.navigationState.index,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// play the transition.
|
||||
Animated.parallel(animations).start(this._onTransitionEnd);
|
||||
}
|
||||
|
||||
render(): ReactElement {
|
||||
@@ -180,6 +200,7 @@ class NavigationAnimatedView
|
||||
const {
|
||||
position,
|
||||
scenes,
|
||||
transition,
|
||||
} = this.state;
|
||||
|
||||
return renderScene({
|
||||
@@ -189,6 +210,7 @@ class NavigationAnimatedView
|
||||
position,
|
||||
scene,
|
||||
scenes,
|
||||
transition,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -203,6 +225,7 @@ class NavigationAnimatedView
|
||||
const {
|
||||
position,
|
||||
scenes,
|
||||
transition,
|
||||
} = this.state;
|
||||
|
||||
return renderOverlay({
|
||||
@@ -212,6 +235,7 @@ class NavigationAnimatedView
|
||||
position,
|
||||
scene: scenes[navigationState.index],
|
||||
scenes,
|
||||
transition,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
@@ -232,6 +256,13 @@ class NavigationAnimatedView
|
||||
|
||||
this.setState({ layout });
|
||||
}
|
||||
|
||||
_onTransitionEnd(): void {
|
||||
const scenes = this.state.scenes.filter(isSceneNotStale);
|
||||
if (scenes.length !== this.state.scenes.length) {
|
||||
this.setState({ scenes });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
@@ -72,6 +72,7 @@ const SceneRenderer = {
|
||||
position: animatedValue.isRequired,
|
||||
scene: scene.isRequired,
|
||||
scenes: PropTypes.arrayOf(scene).isRequired,
|
||||
transition: animatedValue.isRequired,
|
||||
};
|
||||
|
||||
/* NavigationPanPanHandlers */
|
||||
@@ -103,6 +104,7 @@ function extractSceneRendererProps(
|
||||
position: props.position,
|
||||
scene: props.scene,
|
||||
scenes: props.scenes,
|
||||
transition: props.transition,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,6 @@ export type NavigationLayout = {
|
||||
width: NavigationAnimatedValue,
|
||||
};
|
||||
|
||||
export type NavigationPosition = NavigationAnimatedValue;
|
||||
|
||||
export type NavigationScene = {
|
||||
index: number,
|
||||
isStale: boolean,
|
||||
@@ -61,13 +59,20 @@ export type NavigationSceneRendererProps = {
|
||||
onNavigate: NavigationActionCaller,
|
||||
|
||||
// The progressive index of the containing view's navigation state.
|
||||
position: NavigationPosition,
|
||||
position: NavigationAnimatedValue,
|
||||
|
||||
// The scene to render.
|
||||
scene: NavigationScene,
|
||||
|
||||
// All the scenes of the containing view's.
|
||||
scenes: Array<NavigationScene>,
|
||||
|
||||
// The value that represents the progress of the transition when navigation
|
||||
// state changes from one to another. Its numberic value will range from 0
|
||||
// to 1.
|
||||
// transition.__getAnimatedValue() < 1 : transtion is happening.
|
||||
// transition.__getAnimatedValue() == 1 : transtion completes.
|
||||
transition: NavigationAnimatedValue,
|
||||
};
|
||||
|
||||
export type NavigationPanPanHandlers = {
|
||||
@@ -85,6 +90,12 @@ export type NavigationPanPanHandlers = {
|
||||
onStartShouldSetResponderCapture: Function,
|
||||
};
|
||||
|
||||
export type NavigationTransitionSpec = {
|
||||
duration: number,
|
||||
// An easing function from `Easing`.
|
||||
easing: () => any,
|
||||
};
|
||||
|
||||
// Functions.
|
||||
|
||||
export type NavigationActionCaller = Function;
|
||||
@@ -112,3 +123,5 @@ export type NavigationSceneRenderer = (
|
||||
export type NavigationStyleInterpolator = (
|
||||
props: NavigationSceneRendererProps,
|
||||
) => Object;
|
||||
|
||||
export type NavigationTransitionConfigurator = () => NavigationTransitionSpec;
|
||||
|
||||
@@ -72,6 +72,9 @@ function NavigationScenesReducer(
|
||||
nextState: NavigationParentState,
|
||||
prevState: ?NavigationParentState,
|
||||
): Array<NavigationScene> {
|
||||
if (prevState === nextState) {
|
||||
return scenes;
|
||||
}
|
||||
|
||||
const prevScenes = new Map();
|
||||
const freshScenes = new Map();
|
||||
|
||||
Reference in New Issue
Block a user