React Navigation源代码阅读 : views/CardStack/CardStackStyleInterpolator.js

import {I18nManager} from 'react-native';
import getSceneIndicesForInterpolationInputRange from 
'../../utils/getSceneIndicesForInterpolationInputRange';

/**
 * 工具类:构建 card stack 中 card 的样式,在屏幕切换时使用的插值器 screenInterpolator
 * Utility that builds the style for the card in the cards stack.
 *
 * card stack, 卡片栈, 可以理解成将很多卡片通过栈管理起来,
 * 栈顶的那个卡片就是 Focused Card , 也就是用户当前正在操作的那个场景屏幕的卡片
 *
 *     +------------+
 *   +-+            |
 * +-+ |            |
 * | | |            |
 * | | |  Focused   |
 * | | |   Card     |
 * | | |            |
 * +-+ |            |
 *   +-+            |
 *     +------------+
 */

/**
 *
 * 初始布局还没有计算出来的时候使用的样式
 * Render the initial style when the initial layout isn't measured yet.
 */
function forInitial(props) {
    const {navigation, scene} = props;

    const focused = navigation.state.index === scene.index;
    const opacity = focused ? 1 : 0; // 不透明度,1:完全不透明,0:完全透明
    // If not focused, move the scene far away.
    const translate = focused ? 0 : 1000000;
    return {
        opacity,
        transform: [{translateX: translate}, {translateY: translate}],
    };
}

/**
 * Standard iOS-style slide in from the right.
 * 同时动画操作 opacity 和 translateX
 * 1.iOS 上的标准样式:屏幕自右向左滑出,
 * 2.在 Android 上也可以使用
 * @param props
 * @return {*}
 */
function forHorizontal(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 translateX = position.interpolate({
        inputRange: [first, index, last],
        outputRange: I18nManager.isRTL
            ? [-width, 0, width * 0.3]
            : [width, 0, width * -0.3],
    });
    const translateY = 0;

    return {
        opacity,
        transform: [{translateX}, {translateY}],
    };
}

/**
 * Standard iOS-style slide in from the bottom (used for modals).
 * 同时动画操作 opacity 和 translateY
 * 1. iOS 上标准的模态(modal)屏幕的弹出方式:自底向上弹出
 * 2. Android 上也可以使用
 *
 * @param props
 * @return {*}
 */
function forVertical(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 height = layout.initHeight;
    const translateY = position.interpolate({
        inputRange: [first, index, last],
        outputRange: [height, 0, 0],
    });
    const translateX = 0;

    return {
        opacity,
        transform: [{translateX}, {translateY}],
    };
}

/**
 * Standard Android-style fade in from the bottom.
 * 同时动画操作 opacity 和 translateY
 * 1. Android 上标准的屏幕从底部淡入的方式
 * 2. iOS 上也可以使用
 * @param props
 * @return {*}
 */
function forFadeFromBottomAndroid(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 inputRange = [first, index, last - 0.01, last];

    const opacity = position.interpolate({
        inputRange,
        outputRange: [0, 1, 1, 0],
    });

    const translateY = position.interpolate({
        inputRange,
        outputRange: [50, 0, 0, 0],
    });
    const translateX = 0;

    return {
        opacity,
        transform: [{translateX}, {translateY}],
    };
}

/**
 * fadeIn and fadeOut
 * 淡入和淡出,元素坐标无变化,透明度(opacity)变化
 * @param props
 * @return {*}
 */
function forFade(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, index, last],
        outputRange: [0, 1, 1],
    });

    return {
        opacity,
    };
}

/**
 * 是否可以使用原生动画驱动,
 * iOS和Android平台上都可以使用原生动画驱动,所以此值总是返回 true,
 * @return {boolean}
 */
function canUseNativeDriver() {
    // The native driver can be enabled for this interpolator animating
    // opacity, translateX, and translateY is supported by the native animation
    // driver on iOS and Android.
    return true;
}

/**
 * 导出所定义的屏幕插值器 screenInterpolator 和其他工具函数
 */
export default {
    forHorizontal,
    forVertical,
    forFadeFromBottomAndroid,
    forFade,
    canUseNativeDriver,
};

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/80494169