react-native开发React Native开发经验集React Native开发

react-navigation转场动画效果(跨页跳转和跨页回跳

2018-07-05  本文已影响259人  cp__kong

在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,开发时间也不长,上面的总结如有不正确或者更好的方案,希望大神们多多指教。

上一篇下一篇

猜你喜欢

热点阅读