React Native 加载动画
第一步:屏蔽react-navigation默认的过渡动画
在创建createStackNavigator的时候,我们可以设置其相应的属性值,其中有一个transitionConfig的属性。官方解释为它是一个用于返回屏幕过渡对象的函数,该对象中也包含了其他的属性,如下:
/**
* Describes a visual transition from one screen to another.
*/
declare export type TransitionConfig = {
// The basics properties of the animation, such as duration and easing
transitionSpec?: NavigationTransitionSpec,
// How to animate position and opacity of the screen
// based on the value generated by the transitionSpec
screenInterpolator?: (props: NavigationSceneRendererProps) => {},
// How to animate position and opacity of the header componetns
// based on the value generated by the transitionSpec
headerLeftInterpolator?: (props: NavigationSceneRendererProps) => {},
headerTitleInterpolator?: (props: NavigationSceneRendererProps) => {},
headerRightInterpolator?: (props: NavigationSceneRendererProps) => {},
// The style of the container. Useful when a scene doesn't have
// 100% opacity and the underlying container is visible.
containerStyle?: ViewStyleProp,
};
其中我们可以看一下screenInterpolator的作用,它是用来配置屏幕过渡动画的。因为我们需要实现特殊的屏幕过渡动画效果,那么我恩需要将默认的过渡动画给取消掉,代码如下:
const TransitionConfiguration = () => ({
screenInterpolator: (sceneProps) => {
const {scene} = sceneProps
const {route} = scene
const params = route.params || {}
const routeName = sceneProps.scene.route.routeName
const transition = params.transition || 'forHorizontal'
if (routeName === 'SecondPage'){ \\当进入SecondPage页面的时候,取消默认的过渡动画
return null
}
return StackViewStyleInterpolator[transition](sceneProps)
},
transitionSpec: {
duration: 350,
easing: Easing.out(Easing.poly(4)),
timing: Animated.timing,
}
})
const StackNavigatorConfig = {
initialRouteName:'FirstPage',
headerMode: 'screen',
mode:'card',
transitionConfig: TransitionConfiguration,
}
同时我们需要注意一下StackViewStyleInterpolator的引用:
在react-navigation 2.11.2之前:
import StackViewStyleInterpolator from 'react-navigation/src/views/StackView/StackViewStyleInterpolator'
在react-navigation 2.11.2之后:
import StackViewStyleInterpolator from 'react-navigation-stack/dist/views/StackView/StackViewStyleInterpolator'
第二步:自定义过渡动画
这一步我们需要根据实际的场景来实现,一般由设计师来决定。其实我们是没有办法自定义这个过渡动画的,除非去修改源码,但是那样做的难度和风险都比较大,为了实现这样的效果,我只是在第一个页面过渡之前加载了一段动画,在动画结束之后再进行跳转,因为取消了react-navigation默认的过渡动画,所以可以快速的展示出第二个页面。同时为了保证整个过渡过程的流畅性,第一个页面结束时的样子和第二个页面开始时的样子保持一致就OK。
第一个页面的动画是在点击了某一个item之后,在页面的最上面一层添加一个全屏的View进行动画,跟ListView没有任何关系。你们会发现有一些共用的地方(图片+名称),这其实是使用RN提供的UIManager.measure方法获取到点击事件的位置,然后计算出图标和名称的位置,在最上面一层的View渲染另一个图标和名称,从而做到以假乱真的目的。
因为该过渡动画的重用性不太高,这里就不全部展示出来了。
_startAnimated (rowData, event) {
this.props.navigation.setParams({enable: true})
UIManager.measure(event.target, (x, y, width, height, pageX, pageY) => {
this.setState({
isShowItemModel: true,
currentDataSource: rowData,
currentClickY: pageY - 64,
showReView: false,
})
})
}
在动画结束之后,执行跳转动作。
this.props.navigation.navigate('SecondPage')
需要注意的是:
因为上层的View只覆盖了高航栏以下的部分所以导航栏上面的按钮还是可以点击的,所以在执行动画的过程中需要禁止掉按钮点击事件。因为导航按钮是在react-navigation属性中配置的,所以只能在this.props.navigation.params中添加一个变量来控制按钮的点击
static navigationOptions = ({navigation, screenProps}) => ({
title: 'FirstPage', // 固定标题
headerRight: <TouchableOpacity style={{marginRight:20}}
disabled={navigation.state.params ? navigation.state.params.disable : false}
onPress={() => {
const {params = {}} = navigation.state
params.showAlert()
}}><Text style={{fontSize: 14,color: '#333'}}>新增</Text></TouchableOpacity>
})
componentDidMount () {
this.props.navigation.setParams({showAlert: this.showAlert.bind(this),disable: false})
}
goSecondPage() {
this.props.navigation.setParams({disable: true})
this.staggerAnimated.start(()=>{
this.props.navigation.navigate('SecondPage')
this.props.navigation.setParams({disable: false})
this.staggerAnimated.reset()
})
}