前序
优秀的交互设计师在参考了京东的过渡动画后也想在我们的产品上增加类型的效果,在经过一番的Google和思考后觉得还是可以实现的,难度没有想象中的那么大,这里只是做一下总结。
效果图
GIF画质较渣,在模拟器上会出现闪动一下,但是在真机上不会出现,可以忽略这一点。
屏蔽react-navigation默认的过渡动画
关于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: {
const {params = {}} = navigation.state
params.showAlert()
}}>新增
})
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()
})
}
结束语
该过渡动画的实现也不是太过于复杂,希望这篇文章给你有帮助。写了一个简单的小Demo,希望可以帮助你理解。