RN开发(六:自定义React Navigation 过场动画)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010377383/article/details/85050727

(一) 前言
版本环境

node 8.0+(v8.12.0)
npm 5.0+(v6.4.1)
java (v1.8.0_172)

"react-native": "0.57.1",
"react": "16.5.0",
"react-navigation": "^2.18.0",

(二) 需求

  1. 为了统一安卓端card过场动画(安卓类似modal切换), 需要给定义transitionConfig,给两端一样的切换动画,
  2. 对于某些card切换,需要reverse动画效果, 比如:需要将默认的右边滑入进入左侧,反转为左边进入右侧
  3. 对于一些通用的search页面,在tabbar(在4个根页面) 同时注入, 大概率原因是, 直接使用modal放入顶层,在search中再次跳转详情,是无法返回到上次页面的,maybe是路由使用姿势不对,所以这里:需要提供页面card,可以选择过渡动画的方案

(三) 实现

基于上述三个需要,我们需要自定义页面动画,首先

  1. 在src/constants/theme.js中,我们定义常量文件
/* eslint-disable import/no-extraneous-dependencies */
import CardStackStyleInterpolator from 'react-navigation-stack/dist/views/StackView/StackViewStyleInterpolator';
import { Easing, Animated, Platform } from 'react-native';

// animate
const forReverseHorizontal = 'forReverseHorizontal';
const forReverseVertical = 'forReverseVertical';

export default {
  // ...
  // transition
  transitionForHorizontal: 'forHorizontal',
  transitionForVertical: 'forVertical',
  transitionForFadeFromBottomAndroid: 'forFadeFromBottomAndroid',
  transitionForReverseHorizontal: forReverseHorizontal,
  transitionForReverseVertical: forReverseVertical,
  // transition config
  transitionConfig: () => {
    return {
      transitionSpec: {
        duration: 500,
        easing: Easing.out(Easing.poly(4)),
        timing: Animated.timing,
        useNativeDriver: true,
      },
      screenInterpolator: (sceneProps) => {
        const { position, layout, scene, index, scenes } = sceneProps;
        // Use default animate 需求3
        const { route } = scene;
        const params = route.params || {};
        if (params.transition) {
          return CardStackStyleInterpolator[params.transition](sceneProps);
        }

        const toIndex = index;
        const thisSceneIndex = scene.index;
        const {
          initHeight: height,
          initWidth: width,
        } = layout || {};

        // Use custom reverse animate 需求2
        if (params.reverseTransition) {
          const translateX = position.interpolate({
            inputRange: [thisSceneIndex - 1, thisSceneIndex, thisSceneIndex + 1],
            outputRange: [-width, 0, 0],
          });

          if (params.reverseTransition === forReverseHorizontal) {
            return { transform: [{ translateX }] };
          }
          const translateY = position.interpolate({
            inputRange: [0, thisSceneIndex],
            outputRange: [-height, 0],
          });

          if (params.reverseTransition === forReverseVertical) {
            return { transform: [{ translateY }] };
          }
        }

        // Use custom animate 需求1
        const translateX = position.interpolate({
          inputRange: [thisSceneIndex - 1, thisSceneIndex, thisSceneIndex + 1],
          outputRange: [width, 0, 0],
        });

        // Since we want the card to take the same amount of time
        // to animate downwards no matter if it's 3rd on the stack
        // or 53rd, we interpolate over the entire range from 0 - thisSceneIndex
        const translateY = position.interpolate({
          inputRange: [0, thisSceneIndex],
          outputRange: [height, 0],
        });

        const slideFromRight = { transform: [{ translateX }] };
        const slideFromBottom = { transform: [{ translateY }] };

        const lastSceneIndex = scenes[scenes.length - 1].index;

        // Test whether we're skipping back more than one screen
        // and slide from bottom if true
        if (lastSceneIndex - toIndex > 1) {
          // Do not transoform the screen being navigated to
          if (scene.index === toIndex) return;
          // Hide all screens in between
          if (scene.index !== lastSceneIndex) return { opacity: 0 };
          // Slide top screen down
          return slideFromBottom;
        }
        // Otherwise slide from right
        return slideFromRight;
      },
    };
  },
};

  1. 覆盖card默认动画方案
import React from 'react';
import { createStackNavigator } from 'react-navigation';

// constants
import { routers, theme } from 'constants';

// common card
import SearchScreen from '../../Cards/Search';
import MineScreen from '../../Cards/Mine';

// picture card
import PictureScreen from './Picture';

const RouteConfig = {
  // common card
  [routers.search]: SearchScreen,
  [routers.mine]: MineScreen,
  // ....
  // picture card
  [routers.pictureType]: PictureTypeScreen,
 // ....
};

const navigatorConfig = {
  initialRouteName: routers.pictureType,
  cardStyle: { shadowColor: 'transparent' },
  mode: 'card',
  // headerMode: 'none',
  transitionConfig: theme.transitionConfig, // 覆盖动画
 // ....
};

const PictureStack = createStackNavigator(RouteConfig, navigatorConfig);

// ....
export default PictureStack;
  1. 使用动画,

对于需求1. 我们 使用transitionConfig覆盖了 所以默认已经支持双端

对于需求2. 我们 这样通用params的 reverseTransition 判断使用自定义动画反转

navigate(routers.mine, { reverseTransition: theme.transitionForReverseHorizontal })

对于需求3. 我们 这样通用params的 transition 使用内置动画将card变为modal

navigate(routers.search, { transition: theme.transitionForVertical })

(四) 结语

通过自定义过渡动画, 目前可以自定义从四个方向进入,如果还不满足需求,需要自定义修改transitionConfig 方法

猜你喜欢

转载自blog.csdn.net/u010377383/article/details/85050727