在react-native开发过程中,往往会遇到产品的各种关于转场动画的需求,比如登录页需要从地步modal上来,又或者有这种需求,push的页面是A->B->C->D,然后他要求pop回去的路线是
D->B->A,这就是跨页回跳,或者还有这种需求,push的页面是A->B->D,然后pop的路线是
D->C->B->A,C页面在push过程中并不存在。
1.modal
效果
react-navigation的StackNavigator
下的属性mode
可以设置转场效果,但是这边一设置,就是这个导航下所有页面的效果都是这个,也就是要么全是push,要么全是modal,这确实比较坑,不知道最新版的有没有其他解决方案。
最终我找到了transitionConfig
这个属性,这个属性可以自定义转场动画,我在navigate
方法中传递一个参数为isModal
的参数,默认为false
,当需要modal
的时候
this.props.navigation.navigate('XXX',{isModal:true})
然后在router
那设置transitionConfig
const TransitionConfiguration = () => (
{
containerStyle:{},
screenInterpolator: (sceneProps) => {
global.sceneProps = sceneProps
const { scene } = sceneProps;
const { route } = scene;
const params = route.params || {};
const isModal = params.isModal;
if (isModal){
//当为`true`时,采用`modal`效果
return CardStackStyleInterpolator.forVertical(sceneProps);
}else {
return CardStackStyleInterpolator.forHorizontal(sceneProps);
}
},
2.跨页回跳
push:A->B->C->D pop:D->B->A
起初我们找到了goback()
方法可以传key
,所以采用了在需要回跳的页面设置key
,然后在之后的页面回跳回来直接使用goback(key)
,这样就能回到设置key
的页面。
上面是一种思路,这是由上一个的页面控制下一个页面是否需要保持key,是否需要之后的页面直接回跳到这里,前两天又看了遍react-navigation的api,
pop: (n, params) =>
navigation.dispatch(
NavigationActions.pop({ n, immediate: params && params.immediate })
),
发现这个api可以传n,经过尝试是可以直接传n,控制回跳页面数量,当传超过栈的数量时,直接回道栈底。但是这是由后级页面决定回跳几个页面的,逻辑判断要写在触发回跳的页面。
3.跨页跳转
push:A->B->D pop:D->C->B->A
解决方案
我仔细看了react-navigation的底层文件,发现了他transitionConfig
属性可以配置转场动画,CardStackStyleInterpolator
对象下默认有5种动画
export default {
forHorizontal, //水平,正常push,从右往左
forVertical, //modal模式,从下到上
forFadeFromBottomAndroid, //安卓的效果
forFade, //fade模式
canUseNativeDriver, //闪一下
};
然后我加了一种模式forHorizontalBack
,效果类似back返回,实际上是push,新增了一个方法
function forHorizontalBack(props) {
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, first + 0.01, index, last - 0.01, last],
outputRange: [0, 1, 1, 0.85, 0],
});
const width = layout.initWidth;
const { scenes } = props;
const lastSceneIndexInScenes = scenes.length - 1;
const isBack = !scenes[lastSceneIndexInScenes].isActive;
var translateX
if (isBack){
//改了动画后造成back的时候pop动画变成了push效果,所以在这边判断,系数用之前的,效果为pop效果
translateX = position.interpolate({
inputRange: [first, index, last],
outputRange: I18nManager.isRTL
? [-width, 0, width * 0.3]
: [width, 0, width * -0.3],
});
}else {
translateX = position.interpolate({
inputRange: [first, index, last],
outputRange: I18nManager.isRTL
? [-width, 0, width * 0.3]
: [width * -0.7, 0, width],//修改了这个系数,效果是本来push从右往左变成从左往右
});
}
const translateY = 0;
return {
opacity,
transform: [{ translateX }, { translateY }],
};
}
然后在router
那设置transitionConfig
const TransitionConfiguration = () => (
{
containerStyle:{},
screenInterpolator: (sceneProps) => {
global.sceneProps = sceneProps
const { scene } = sceneProps;
const { route } = scene;
const params = route.params || {};
const isModal = params.isModal;
const isSpan = params.span;
if(isSpan){
return CardStackStyleInterpolator.forHorizontalBack(sceneProps);
}
if (isModal){
return CardStackStyleInterpolator.forVertical(sceneProps);
}else {
return CardStackStyleInterpolator.forHorizontal(sceneProps);
}
},
})
因为本人原来是做iOS的,项目需要才开始摸索react-native,开发时间也不长,上面的总结如有不正确或者更好的方案,希望大神们多多指教。