版权声明:本文为博主原创文章,未经博主允许不得转载。 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",
(二) 需求
- 为了统一安卓端card过场动画(安卓类似modal切换), 需要给定义transitionConfig,给两端一样的切换动画,
- 对于某些card切换,需要reverse动画效果, 比如:需要将默认的右边滑入进入左侧,反转为左边进入右侧
- 对于一些通用的search页面,在tabbar(在4个根页面) 同时注入, 大概率原因是, 直接使用modal放入顶层,在search中再次跳转详情,是无法返回到上次页面的,maybe是路由使用姿势不对,所以这里:需要提供页面card,可以选择过渡动画的方案
(三) 实现
基于上述三个需要,我们需要自定义页面动画,首先
- 在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;
},
};
},
};
- 覆盖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. 我们 使用transitionConfig覆盖了 所以默认已经支持双端
对于需求2. 我们 这样通用params的 reverseTransition 判断使用自定义动画反转
navigate(routers.mine, { reverseTransition: theme.transitionForReverseHorizontal })
对于需求3. 我们 这样通用params的 transition 使用内置动画将card变为modal
navigate(routers.search, { transition: theme.transitionForVertical })
(四) 结语
通过自定义过渡动画, 目前可以自定义从四个方向进入,如果还不满足需求,需要自定义修改transitionConfig 方法