import { Animated, Easing, Platform } from 'react-native'; import StyleInterpolator from './StackViewStyleInterpolator'; import { supportsImprovedSpringAnimation } from '../../utils/ReactNativeFeatures'; import { TransitionProps, TransitionConfig } from '../../types'; let IOSTransitionSpec; if (supportsImprovedSpringAnimation()) { // These are the exact values from UINavigationController's animation configuration IOSTransitionSpec = { timing: Animated.spring, stiffness: 1000, damping: 500, mass: 3, overshootClamping: true, restDisplacementThreshold: 0.01, restSpeedThreshold: 0.01, }; } else { // This is an approximation of the IOS spring animation using a derived bezier curve IOSTransitionSpec = { duration: 500, easing: Easing.bezier(0.2833, 0.99, 0.31833, 0.99), timing: Animated.timing, }; } // Standard iOS navigation transition const SlideFromRightIOS = { transitionSpec: IOSTransitionSpec, screenInterpolator: StyleInterpolator.forHorizontal, containerStyle: { backgroundColor: '#eee', }, }; // Standard iOS navigation transition for modals const ModalSlideFromBottomIOS = { transitionSpec: IOSTransitionSpec, screenInterpolator: StyleInterpolator.forVertical, containerStyle: { backgroundColor: '#eee', }, }; // Standard Android navigation transition when opening an Activity const FadeInFromBottomAndroid = { // See http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/res/res/anim/activity_open_enter.xml transitionSpec: { duration: 350, easing: Easing.out(Easing.poly(5)), // decelerate timing: Animated.timing, }, screenInterpolator: StyleInterpolator.forFadeFromBottomAndroid, }; // Standard Android navigation transition when closing an Activity const FadeOutToBottomAndroid = { // See http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/res/res/anim/activity_close_exit.xml transitionSpec: { duration: 150, easing: Easing.in(Easing.linear), // accelerate timing: Animated.timing, }, screenInterpolator: StyleInterpolator.forFadeToBottomAndroid, }; const NoAnimation = { transitionSpec: { duration: 0, timing: Animated.timing, }, screenInterpolator: StyleInterpolator.forNoAnimation, containerStyle: { backgroundColor: '#eee', }, }; function defaultTransitionConfig( transitionProps: TransitionProps, prevTransitionProps?: TransitionProps, isModal?: boolean ): TransitionConfig { if (Platform.OS !== 'ios') { // Use the default Android animation no matter if the screen is a modal. // Android doesn't have full-screen modals like iOS does, it has dialogs. if ( prevTransitionProps && transitionProps.index < prevTransitionProps.index ) { // Navigating back to the previous screen return FadeOutToBottomAndroid; } return FadeInFromBottomAndroid; } // iOS and other platforms if (isModal) { return ModalSlideFromBottomIOS; } return SlideFromRightIOS; } function getTransitionConfig( transitionConfigurer: | undefined | (( transitionProps: TransitionProps, prevTransitionProps?: TransitionProps, isModal?: boolean ) => T), transitionProps: TransitionProps, prevTransitionProps?: TransitionProps, isModal?: boolean ): TransitionConfig & T { const defaultConfig = defaultTransitionConfig( transitionProps, prevTransitionProps, isModal ); if (transitionConfigurer) { return { ...defaultConfig, ...transitionConfigurer(transitionProps, prevTransitionProps, isModal), }; } return defaultConfig as any; } export default { defaultTransitionConfig, getTransitionConfig, SlideFromRightIOS, ModalSlideFromBottomIOS, FadeInFromBottomAndroid, FadeOutToBottomAndroid, NoAnimation, };