跳转到主要内容
版本:7.x

堆栈导航器

堆栈导航器提供了一种在屏幕之间转换的方式,其中每个新屏幕都放置在堆栈的顶部。

默认情况下,堆栈导航器配置为具有熟悉的 iOS 和 Android 外观和感觉:新屏幕在 iOS 上从右侧滑入,在 Android 上使用操作系统默认动画。但是,动画可以自定义以满足您的需求。

需要记住的一件事是,虽然 @react-navigation/stack 非常可定制,但它是用 JavaScript 实现的。虽然它使用原生方式运行动画和手势,但性能可能不如原生实现快。对于许多应用程序来说,这可能不是问题,但是如果您在导航期间遇到性能问题,请考虑使用 @react-navigation/native-stack 代替 - 它使用原生导航原语。

安装

要使用此导航器,请确保您已安装 @react-navigation/native 及其依赖项(请遵循本指南),然后安装 @react-navigation/stack

npm install @react-navigation/stack

然后,您需要安装和配置堆栈导航器所需的库

  1. 首先,安装 react-native-gesture-handler

    如果您有 Expo 管理的项目,请在您的项目目录中运行

    npx expo install react-native-gesture-handler

    如果您有一个裸 React Native 项目,请在您的项目目录中运行

    npm install react-native-gesture-handler
  2. 为了完成 react-native-gesture-handler 的安装,我们需要有条件地导入它。为此,创建 2 个文件

    gesture-handler.native.js
    // Only import react-native-gesture-handler on native platforms
    import 'react-native-gesture-handler';
    gesture-handler.js
    // Don't import react-native-gesture-handler on web

    现在,在您的入口文件(例如 index.jsApp.js)的顶部添加以下内容(确保它在顶部,并且前面没有任何其他内容)

    import './gesture-handler';

    由于堆栈导航器在 Web 上不使用 react-native-gesture-handler,这避免了不必要地增加捆绑包大小。

    警告

    如果您正在为 Android 或 iOS 构建,请不要跳过此步骤,否则您的应用可能会在生产环境中崩溃,即使它在开发环境中运行良好。这不适用于其他平台。

  3. 可选地,您还可以安装 @react-native-masked-view/masked-view。如果您想为标题使用 UIKit 样式动画(HeaderStyleInterpolators.forUIKit),则需要这样做。

    如果您有 Expo 管理的项目,请在您的项目目录中运行

    npx expo install @react-native-masked-view/masked-view

    如果您有一个裸 React Native 项目,请在您的项目目录中运行

    npm install @react-native-masked-view/masked-view
  4. 如果您在 Mac 上并且正在为 iOS 开发,您还需要安装 pod(通过 Cocoapods)以完成链接。

    npx pod-install ios

用法

要使用此导航器,请从 @react-navigation/stack 导入它

import { createStackNavigator } from '@react-navigation/stack';

const MyStack = createStackNavigator({
screens: {
Home: HomeScreen,
Profile: ProfileScreen,
},
});
Snack 上尝试

API 定义

Props

除了所有导航器共享的通用 props 之外,堆栈导航器还接受以下附加 props

detachInactiveScreens

布尔值,用于指示是否应将非活动屏幕从视图层次结构中分离以节省内存。这实现了与 react-native-screens 的集成。默认为 true

如果您需要为特定屏幕禁用此优化(例如,您希望屏幕即使在未聚焦时也保持在视图中),请使用 detachPreviousScreen 选项。

选项

以下 选项 可用于配置导航器中的屏幕。这些可以在 Stack.navigatorscreenOptions prop 或 Stack.Screenoptions prop 中指定。

title

可以用作 headerTitle 后备的字符串。

cardShadowEnabled

使用此 prop 在过渡期间显示可见阴影。默认为 true

cardOverlayEnabled

使用此 prop 在过渡期间使半透明的深色叠加层在卡片下方可见。在 Android 上默认为 true,在 iOS 上默认为 false

cardOverlay

返回 React Element 的函数,以显示为卡片的叠加层。使用此功能时,请确保将 cardOverlayEnabled 设置为 true

cardStyle

堆栈中卡片的样式对象。您可以提供自定义背景颜色来代替此处的默认背景。

您还可以指定 { backgroundColor: 'transparent' } 使上一个屏幕在下方可见(对于透明模态框)。这对于实现模态对话框之类的东西很有用。当使用透明背景时,您还应该在选项中指定 presentation: 'modal',以便上一个屏幕不会分离并保持在下方可见。

在 Web 上,屏幕的高度不限于视口的高度。这是设计使然,允许浏览器地址栏在滚动时隐藏。如果这不是理想的行为,您可以将 cardStyle 设置为 { flex: 1 } 以强制屏幕填充视口。

presentation

这是一个快捷选项,它配置多个选项以配置渲染和过渡的样式

  • card:为 iOS 和 Android 屏幕过渡使用默认操作系统动画。
  • modal:使用模态动画。这会改变一些事情
    • 除非另有说明,否则将屏幕的 headerMode 设置为 screen
    • 更改屏幕动画以匹配模态框的平台行为。
  • transparentModal:类似于 modal。这会更改以下内容
    • 除非另有说明,否则将屏幕的 headerMode 设置为 screen
    • 将屏幕的背景颜色设置为透明,以便上一个屏幕可见
    • 调整 detachPreviousScreen 选项,以便上一个屏幕保持渲染。
    • 防止上一个屏幕从其最后位置动画。
    • 将屏幕动画更改为垂直滑动动画。

有关如何自定义 transparentModal 的更多详细信息,请参阅 透明模态框

animationTypeForReplace

当此屏幕替换另一个屏幕时要使用的动画类型。它采用以下值

  • push - 将使用新屏幕被推送的动画
  • pop - 将使用屏幕被弹出的动画

默认为 push

当使用 pop 时,pop 动画将应用于被替换的屏幕。

gestureEnabled

是否可以使用手势关闭此屏幕。在 iOS 上默认为 true,在 Android 上默认为 false

Web 上不支持手势。

gestureResponseDistance

数字,用于覆盖从屏幕边缘开始触摸以识别手势的距离。

它将根据 gestureDirection 值配置水平或垂直距离。

默认值是

  • 50 - 当 gestureDirectionhorizontalhorizontal-inverted
  • 135 - 当 gestureDirectionverticalvertical-inverted

Web 上不支持此功能。

gestureVelocityImpact

数字,它决定了手势速度的相关性。默认为 0.3。

Web 上不支持此功能。

gestureDirection

手势的方向。有关详细信息,请参阅 动画部分

Web 上不支持此功能。

transitionSpec

屏幕过渡的配置对象。有关详细信息,请参阅 动画部分

cardStyleInterpolator

卡片各个部分的插值样式。有关详细信息,请参阅 动画部分

headerStyleInterpolator

标题各个部分的插值样式。有关详细信息,请参阅 动画部分

keyboardHandlingEnabled

如果为 false,则从该屏幕导航到新屏幕时,键盘不会自动消失。默认为 true

detachPreviousScreen

布尔值,用于指示是否将上一个屏幕从视图层次结构中分离以节省内存。如果您需要通过活动屏幕看到上一个屏幕,请将其设置为 false。仅当 detachInactiveScreens 未设置为 false 时适用。

当使用 presentation 作为 transparentModalmodal 以保持所需屏幕可见时,这将自动调整。在其他情况下,默认为 true

freezeOnBlur

布尔值,指示是否阻止非活动屏幕重新渲染。默认为 false。当在应用程序顶部运行来自 react-native-screens 包的 enableFreeze() 时,默认为 true

仅在 iOS 和 Android 上受支持。

您可以在此处找到标题相关选项的列表。这些选项可以在 Stack.navigatorscreenOptions prop 或 Stack.Screenoptions prop 中指定。您不必直接使用 @react-navigation/elements 即可使用这些选项,它们只是在该页面中记录。

除了这些之外,堆栈还支持以下选项

要使用的自定义标题,而不是默认标题。

这接受一个返回 React Element 的函数以显示为标题。该函数接收一个对象,其中包含以下属性作为参数

  • navigation - 当前屏幕的导航对象。
  • route - 当前屏幕的路由对象。
  • options - 当前屏幕的选项
  • layout - 屏幕的尺寸,包含 heightwidth 属性。
  • progress 表示动画进度的动画节点。
  • back - 后退按钮的选项,包含一个对象,其中包含用于后退按钮标签的 title 属性。
  • styleInterpolator - 返回标题中各种元素的插值样式的函数。

使用自定义标题时,请确保也将 headerMode 设置为 screen(有关更多详细信息,请参见下文)。

示例

import { getHeaderTitle } from '@react-navigation/elements';

// ..

header: ({ navigation, route, options, back }) => {
const title = getHeaderTitle(options, route.name);

return (
<MyHeader
title={title}
leftButton={
back ? <MyBackButton onPress={navigation.goBack} /> : undefined
}
style={options.headerStyle}
/>
);
};

要为导航器中所有屏幕设置自定义标题,您可以在导航器的 screenOptions prop 中指定此选项。

使用自定义标题时,有 2 件事情要记住

headerStyle 中指定 height 以避免故障

如果您的标题高度与默认标题高度不同,那么您可能会注意到由于测量是异步的而导致的故障。显式指定高度将避免此类故障。

示例

headerStyle: {
height: 80, // Specify the height of your custom header
};

请注意,默认情况下此样式不应用于标题,因为您可以控制自定义标题的样式。如果您还想将此样式应用于您的标题,请使用 props 中的 headerStyle

headerMode 设置为 float 以进行自定义标题动画

默认情况下,有一个浮动标题,它为 iOS 上非模态框的多个屏幕渲染标题。这些标题包括动画,可以平滑地切换到另一个标题。

如果您指定自定义标题,React Navigation 会自动将其更改为 screen,以便标题与屏幕一起动画。这意味着您不必实现动画来单独为其制作动画。

但是您可能希望保留浮动标题,以便在标题之间具有不同的过渡动画。为此,您需要在选项中指定 headerMode: 'float',然后在自定义标题中对 progress.currentprogress.next props 进行插值。例如,以下将交叉淡化标题

const opacity = Animated.add(progress.current, progress.next || 0).interpolate({
inputRange: [0, 1, 2],
outputRange: [0, 1, 0],
});

return (
<Animated.View style={{ opacity }}>{/* Header content */}</Animated.View>
);

headerMode

指定应如何渲染标题

  • float - 标题在屏幕上方渲染,并独立于屏幕进行动画处理。这是 iOS 上非模态框的默认设置。
  • screen - 标题作为屏幕的一部分渲染,并与屏幕一起动画。这是其他平台上的默认设置。

headerShown

是否显示或隐藏屏幕的标题。默认情况下显示标题。将其设置为 false 会隐藏标题。

headerBackAllowFontScaling

后退按钮标题字体是否应缩放以尊重文本大小辅助功能设置。默认为 false。

headerBackAccessibilityLabel

标题后退按钮的辅助功能标签。

headerBackImage

返回 React Element 的函数,以在标题的后退按钮中显示自定义图像。当使用函数时,它会在其参数对象中接收 tintColor。默认为带有后退图像源的 Image 组件,它是平台的默认后退图标图像(iOS 上的 chevron 和 Android 上的箭头)。

headerBackTitle

iOS 上后退按钮使用的标题字符串。默认为上一个场景的标题。使用 headerBackButtonDisplayMode 自定义行为。

headerTruncatedBackTitle

headerBackTitle 不适合屏幕时,后退按钮使用的标题字符串。默认情况下为 "Back"

headerBackButtonDisplayMode

后退按钮如何显示图标和标题。

支持的值

  • default:根据可用空间显示以下内容之一:上一个屏幕的标题、通用标题(例如“返回”)或无标题(仅图标)。
  • generic:根据可用空间显示以下内容之一:通用标题(例如“返回”)或无标题(仅图标)。
  • minimal:始终仅显示图标,不显示标题。

在 iOS 上默认为 default,在 Android 上默认为 minimal

headerBackTitleStyle

后退标题的样式对象。

事件

导航器可以在某些操作上发出事件。支持的事件有

transitionStart

当当前屏幕的过渡动画开始时,将触发此事件。

事件数据

  • e.data.closing - 布尔值,指示屏幕是正在打开还是关闭。

示例

React.useEffect(() => {
const unsubscribe = navigation.addListener('transitionStart', (e) => {
// Do something
});

return unsubscribe;
}, [navigation]);

transitionEnd

当当前屏幕的过渡动画结束时,将触发此事件。

事件数据

  • e.data.closing - 布尔值,指示屏幕是已打开还是已关闭。

示例

React.useEffect(() => {
const unsubscribe = navigation.addListener('transitionEnd', (e) => {
// Do something
});

return unsubscribe;
}, [navigation]);

gestureStart

当当前屏幕的滑动开始时,将触发此事件。

示例

React.useEffect(() => {
const unsubscribe = navigation.addListener('gestureStart', (e) => {
// Do something
});

return unsubscribe;
}, [navigation]);

gestureEnd

当当前屏幕的滑动结束时,将触发此事件。例如,屏幕已成功关闭。

示例

React.useEffect(() => {
const unsubscribe = navigation.addListener('gestureEnd', (e) => {
// Do something
});

return unsubscribe;
}, [navigation]);

gestureCancel

当当前屏幕的滑动被取消时,将触发此事件。例如,屏幕未通过手势关闭。

示例

React.useEffect(() => {
const unsubscribe = navigation.addListener('gestureCancel', (e) => {
// Do something
});

return unsubscribe;
}, [navigation]);

助手

堆栈导航器将以下方法添加到导航对象

replace

在堆栈中用新屏幕替换当前屏幕。该方法接受以下参数

  • name - string - 要推送到堆栈的路由名称。
  • params - object - 要传递到目标路由的屏幕参数。
navigation.replace('Profile', { owner: 'Michaś' });

push

将新屏幕推送到堆栈顶部并导航到它。该方法接受以下参数

  • name - string - 要推送到堆栈的路由名称。
  • params - object - 要传递到目标路由的屏幕参数。
navigation.push('Profile', { owner: 'Michaś' });

pop

从堆栈中弹出当前屏幕并导航回上一个屏幕。它接受一个可选参数 (count),允许您指定要弹回多少个屏幕。

navigation.pop();

popTo

通过弹出其后的屏幕导航回堆栈中的上一个屏幕。该方法接受以下参数

  • name - string - 要导航到的路由名称。
  • params - object - 要传递到目标路由的屏幕参数。
  • merge - boolean - 参数是否应与现有路由参数合并,或者替换它们(当导航到现有屏幕时)。默认为 false

如果在堆栈中未找到匹配的屏幕,这将弹出当前屏幕并添加一个具有指定名称和参数的新屏幕。

navigation.popTo('Profile', { owner: 'Michaś' });

popToTop

弹出堆栈中除第一个屏幕之外的所有屏幕并导航到它。

navigation.popToTop();

Hooks

堆栈导航器导出以下 hooks

useCardAnimation

此 hook 返回与屏幕动画相关的值。它包含以下属性

  • current - 当前屏幕的值
    • progress - 表示当前屏幕进度值的动画节点。
  • next - 堆栈中此屏幕之后的屏幕的值。如果动画屏幕是最后一个屏幕,则这可以是 undefined
    • progress - 表示下一个屏幕进度值的动画节点。
  • closing - 表示卡片是否正在关闭的动画节点。关闭时为 1,否则为 0
  • swiping - 表示卡片是否正在滑动的动画节点。滑动时为 1,否则为 0
  • inverted - 表示卡片是否反转的动画节点。反转时为 -1,否则为 1
  • index - 卡片在堆栈中的索引。
  • layouts - 我们用于动画的各种项目的布局测量。
    • screen - 整个屏幕的布局。包含 heightwidth 属性。
  • insets - 安全区域插角的布局。包含 toprightbottomleft 属性。

有关如何使用此 hook 的示例,请参阅 透明模态框

动画

您可以指定 animation 选项来自定义屏幕被推送或弹出的过渡动画。

animation 的支持值是

  • default - 基于平台和操作系统版本的默认动画。
  • fade - 对话框的简单淡入淡出动画。
  • fade_from_bottom - 适用于 Android Oreo 的标准 Android 样式从底部淡入。
  • fade_from_right - 适用于 Android 14 的标准 Android 样式从右侧淡入。
  • reveal_from_bottom - 适用于 Android Pie 的标准 Android 样式从底部显示。
  • scale_from_center - 从中心缩放动画。
  • slide_from_right - 标准 iOS 样式从右侧滑入。
  • slide_from_left - 类似于 slide_from_right,但屏幕将从左侧滑入。
  • slide_from_bottom - 适用于模态框和底部工作表的从底部滑动动画。
  • none - 屏幕会立即推送或弹出,没有任何动画。

默认情况下,Android 和 iOS 使用 default 动画,其他平台使用 none

如果您需要更多地控制动画,可以使用各种与动画相关的选项自定义动画的各个部分

StackNavigator 公开了各种选项,用于配置在添加或删除屏幕时的过渡动画。这些过渡动画可以在每个屏幕的基础上进行自定义,方法是在每个屏幕的 options 属性中指定选项。

  • gestureDirection - 滑动手势的方向

    • horizontal - 关闭屏幕的手势将从左侧开始,在 RTL 布局中从右侧开始。对于动画,屏幕将从右侧滑动,使用 SlideFromRightIOS,在 RTL 布局中从左侧滑动。
    • horizontal-inverted - 关闭屏幕的手势将从右侧开始,在 RTL 布局中从左侧开始。对于动画,屏幕将从左侧滑动,使用 SlideFromRightIOS,在 RTL 布局中从右侧滑动,因为方向已反转。
    • vertical - 关闭屏幕的手势将从顶部开始。对于动画,屏幕将从底部滑动。
    • vertical-inverted - 关闭屏幕的手势将从底部开始。对于动画,屏幕将从顶部滑动。

    您可能希望同时指定匹配的水平/垂直动画以及 gestureDirection。对于库中包含的动画,如果您将 gestureDirection 设置为反转方向之一,它也会翻转动画方向。

  • transitionSpec - 一个对象,用于指定动画类型 (timingspring) 及其选项(例如 timingduration)。它接受 2 个属性

    • open - 添加屏幕时过渡的配置
    • close - 删除屏幕时过渡的配置。

    每个对象都应指定 2 个属性

    • animation - 用于动画的动画函数。支持的值为 timingspring
    • config - 定时函数的配置对象。对于 timing,它可以是 durationeasing。对于 spring,它可以是 stiffnessdampingmassovershootClampingrestDisplacementThresholdrestSpeedThreshold

    使用弹簧动画的配置如下所示

    const config = {
    animation: 'spring',
    config: {
    stiffness: 1000,
    damping: 500,
    mass: 3,
    overshootClamping: true,
    restDisplacementThreshold: 0.01,
    restSpeedThreshold: 0.01,
    },
    };

    我们可以将此配置传递到 transitionSpec 选项中

    <Stack.Screen
    name="Profile"
    component={Profile}
    options={{
    transitionSpec: {
    open: config,
    close: config,
    },
    }}
    />
  • cardStyleInterpolator - 这是一个函数,用于指定卡片各个部分的插值样式。这允许您自定义从一个屏幕导航到另一个屏幕时的过渡效果。它应至少返回一个空对象,可能包含容器、卡片本身、叠加层和阴影的插值样式。支持的属性有

    • containerStyle - 包裹卡片的容器视图的样式。
    • cardStyle - 表示卡片的视图的样式。
    • overlayStyle - 表示下方半透明叠加层的视图的样式
    • shadowStyle - 表示卡片阴影的视图的样式。

    该函数在其参数中接收以下属性

    • current - 当前屏幕的值
      • progress - 表示当前屏幕进度值的动画节点。
    • next - 堆栈中此屏幕之后的屏幕的值。如果动画屏幕是最后一个屏幕,则这可以是 undefined
      • progress - 表示下一个屏幕进度值的动画节点。
    • index - 卡片在堆栈中的索引。
    • closing - 表示卡片是否正在关闭的动画节点。关闭时为 1,否则为 0
    • layouts - 我们用于动画的各种项目的布局测量。
      • screen - 整个屏幕的布局。包含 heightwidth 属性。

    请注意,当屏幕不是最后一个屏幕时,它将使用下一个屏幕的过渡配置。 这是因为许多过渡涉及前一个屏幕的动画,因此这两个过渡需要保持在一起,以防止在两个屏幕上运行两种不同类型的过渡(例如,滑动和模态)。您可以检查 next 参数以了解是否要动画化前一个屏幕。有关此参数的更多信息,请参阅 动画 部分。

    仅淡入淡出屏幕的配置如下所示

    const forFade = ({ current }) => ({
    cardStyle: {
    opacity: current.progress,
    },
    });

    我们可以将此函数传递到 cardStyleInterpolator 选项中

    <Stack.Screen
    name="Profile"
    component={Profile}
    options={{ cardStyleInterpolator: forFade }}
    />

    将为每个屏幕调用插值器。例如,假设您的堆栈中有 2 个屏幕,A 和 B。B 是新进入焦点的屏幕,A 是上一个屏幕。将为每个屏幕调用插值器

    • B 调用插值器:在此处,current.progress 值表示过渡的进度,它将从 0 开始,到 1 结束。由于 B 是最后一个屏幕,因此不会有 next.progress
    • A 调用插值器:在此处,current.progress 将保持在 1 的值,并且不会更改,因为当前过渡正在为 B 运行,而不是为 A 运行。next.progress 值表示 B 的进度,并将从 0 开始,到 1 结束。

    假设我们想要在过渡期间动画化两个屏幕。最简单的方法是将当前屏幕和下一个屏幕的进度值组合起来

    const progress = Animated.add(
    current.progress.interpolate({
    inputRange: [0, 1],
    outputRange: [0, 1],
    extrapolate: 'clamp',
    }),
    next
    ? next.progress.interpolate({
    inputRange: [0, 1],
    outputRange: [0, 1],
    extrapolate: 'clamp',
    })
    : 0
    );

    在此处,屏幕 A 将同时具有 current.progressnext.progress,并且由于 current.progress 保持在 1,而 next.progress 正在变化,因此组合后,进度将从 1 变为 2。屏幕 B 将仅具有 current.progress,它将从 0 变为 1。因此,我们可以为 0-11-2 应用不同的插值,分别动画化聚焦屏幕和未聚焦屏幕。

    将上一个屏幕稍微向左平移,并将当前屏幕从右边缘平移的配置如下所示

    const forSlide = ({ current, next, inverted, layouts: { screen } }) => {
    const progress = Animated.add(
    current.progress.interpolate({
    inputRange: [0, 1],
    outputRange: [0, 1],
    extrapolate: 'clamp',
    }),
    next
    ? next.progress.interpolate({
    inputRange: [0, 1],
    outputRange: [0, 1],
    extrapolate: 'clamp',
    })
    : 0
    );

    return {
    cardStyle: {
    transform: [
    {
    translateX: Animated.multiply(
    progress.interpolate({
    inputRange: [0, 1, 2],
    outputRange: [
    screen.width, // Focused, but offscreen in the beginning
    0, // Fully focused
    screen.width * -0.3, // Fully unfocused
    ],
    extrapolate: 'clamp',
    }),
    inverted
    ),
    },
    ],
    },
    };
    };
  • headerStyleInterpolator - 这是一个函数,用于指定标题各个部分的插值样式。它应至少返回一个空对象,可能包含左侧标签和按钮、右侧按钮、标题和背景的插值样式。支持的属性有

    • leftLabelStyle - 左侧按钮(后退按钮标签)的标签样式。
    • leftButtonStyle - 左侧按钮(通常是后退按钮)的样式。
    • rightButtonStyle - 右侧按钮的样式。
    • titleStyle - 标题文本的样式。
    • backgroundStyle - 标题背景的样式。

    该函数在其参数中接收以下属性

    • current - 当前屏幕(拥有此标题的屏幕)的值。
      • progress - 表示当前屏幕的进度值的动画节点。0 表示屏幕应开始进入视图,0.5 表示屏幕处于中间位置,1 表示屏幕应完全进入视图。
    • next - 堆栈中此屏幕之后的屏幕的值。如果动画屏幕是最后一个屏幕,则这可以是 undefined
      • progress - 表示下一个屏幕进度值的动画节点。
    • layouts - 我们用于动画的各种项目的布局测量。每个布局对象都包含 heightwidth 属性。
      • screen - 整个屏幕的布局。
      • title - 标题元素的布局。当不呈现标题时,可能为 undefined
      • leftLabel - 后退按钮标签的布局。当不呈现后退按钮标签时,可能为 undefined

    仅淡入淡出元素的配置如下所示

    const forFade = ({ current, next }) => {
    const opacity = Animated.add(
    current.progress,
    next ? next.progress : 0
    ).interpolate({
    inputRange: [0, 1, 2],
    outputRange: [0, 1, 0],
    });

    return {
    leftButtonStyle: { opacity },
    rightButtonStyle: { opacity },
    titleStyle: { opacity },
    backgroundStyle: { opacity },
    };
    };

    我们可以将此函数传递到 headerStyleInterpolator 选项中

    <Stack.Screen
    name="Profile"
    component={Profile}
    options={{ headerStyleInterpolator: forFade }}
    />

预制配置

使用这些选项,可以为屏幕构建自定义过渡动画。我们还从库中导出各种配置,其中包含您可以使用的现成动画

TransitionSpecs

  • TransitionIOSSpec - 来自 UINavigationController 动画配置的精确值。
  • FadeInFromBottomAndroidSpec - 来自 Android Nougat 的活动打开动画的配置。
  • FadeOutToBottomAndroidSpec - 来自 Android Nougat 的活动关闭动画的配置。
  • RevealFromBottomAndroidSpec - 来自 Android Pie 的活动打开动画的近似配置。

示例

import { TransitionSpecs } from '@react-navigation/stack';

// ...

<Stack.Screen
name="Profile"
component={Profile}
options={{
transitionSpec: {
open: TransitionSpecs.TransitionIOSSpec,
close: TransitionSpecs.TransitionIOSSpec,
},
}}
/>;

CardStyleInterpolators

  • forHorizontalIOS - 标准 iOS 风格从右侧滑入。
  • forVerticalIOS - 标准 iOS 风格从底部滑入(用于模态)。
  • forModalPresentationIOS - iOS 13 中标准的 iOS 风格模态动画。
  • forFadeFromBottomAndroid - Android Oreo 标准的 Android 风格从底部淡入。
  • forRevealFromBottomAndroid - Android Pie 标准的 Android 风格从底部显露。

Android Oreo 风格垂直屏幕淡入淡出动画的示例配置

import { CardStyleInterpolators } from '@react-navigation/stack';

// ...

<Stack.Screen
name="Profile"
component={Profile}
options={{
title: 'Profile',
cardStyleInterpolator: CardStyleInterpolators.forFadeFromBottomAndroid,
}}
/>;

HeaderStyleInterpolators

  • forUIKit - 用于标题的标准 UIKit 风格动画,其中标题淡入后退按钮标签。
  • forFade - 用于标题元素的简单淡入淡出动画。
  • forStatic - 简单的平移动画,用于沿滑动屏幕平移标题。

标题元素的默认 iOS 动画的示例配置,其中标题淡入后退按钮

import { HeaderStyleInterpolators } from '@react-navigation/stack';

// ...

<Stack.Screen
name="Profile"
component={Profile}
options={{
title: 'Profile',
headerStyleInterpolator: HeaderStyleInterpolators.forUIKit,
}}
/>;
警告

始终在文件的顶层定义动画配置,以确保引用在重新渲染之间不会更改。这对于平滑可靠的过渡动画非常重要。

TransitionPresets

我们导出各种过渡预设,这些预设将各种选项捆绑在一起,以匹配某些原生动画。过渡预设是一个对象,其中包含在 TransitionPresets 下导出的几个与动画相关的屏幕选项。目前,以下预设可用

  • SlideFromRightIOS - 标准 iOS 导航过渡。
  • ModalSlideFromBottomIOS - 用于模态的标准 iOS 导航过渡。
  • ModalPresentationIOS - 标准 iOS 模态演示风格(在 iOS 13 中引入)。
  • FadeFromBottomAndroid - 在 Android < 9 (Oreo) 上打开或关闭 Activity 时的标准 Android 导航过渡。
  • RevealFromBottomAndroid - 在 Android 9 (Pie) 上打开或关闭 Activity 时的标准 Android 导航过渡。
  • ScaleFromCenterAndroid - 在 Android >= 10 上打开或关闭 Activity 时的标准 Android 导航过渡。
  • DefaultTransition - 当前平台的默认导航过渡。
  • ModalTransition - 当前平台的默认模态过渡。

您可以在 options 中展开这些预设,以自定义屏幕的动画

import { TransitionPresets } from '@react-navigation/stack';

// ...

<Stack.Screen
name="Profile"
component={Profile}
options={{
title: 'Profile',
...TransitionPresets.ModalSlideFromBottomIOS,
}}
/>;

如果您想自定义导航器中所有屏幕的过渡动画,可以在导航器的 screenOptions 属性中指定它。

iOS 模态演示风格的示例配置

import { TransitionPresets } from '@react-navigation/stack';

// ...

<Stack.Navigator
initialRouteName="Home"
screenOptions={({ route, navigation }) => ({
headerShown: false,
gestureEnabled: true,
...TransitionPresets.ModalPresentationIOS,
})}
>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Profile" component={Profile} />
</Stack.Navigator>;

透明模态

透明模态就像覆盖屏幕的模态对话框。之前的屏幕仍然在下方可见。要获得透明模态屏幕,您可以在屏幕的选项中指定 presentation: 'transparentModal'

示例

<Stack.Navigator>
<Stack.Screen name="Home" component={HomeStack} />
<Stack.Screen
name="Modal"
component={ModalScreen}
options={{ presentation: 'transparentModal' }}
/>
</Stack.Navigator>

现在,当您导航到 Modal 屏幕时,它将具有透明背景,并且 Home 屏幕将在下方可见。

除了 presentation 之外,您可能还需要选择性地指定更多内容以获得类似模态对话框的行为

  • 使用 headerShown: false 禁用标题
  • 使用 cardOverlayEnabled: true 启用叠加层(您无法通过点击叠加层来关闭屏幕,有关替代方法,请参见下文)

如果您想进一步自定义对话框的动画方式,或者想要在点击叠加层时关闭屏幕等,可以使用 useCardAnimation hook 自定义屏幕内部的元素。

示例

import { Animated, View, Text, Pressable, StyleSheet } from 'react-native';
import { useTheme, useNavigation } from '@react-navigation/native';
import { useCardAnimation } from '@react-navigation/stack';
import { Button } from '@react-navigation/elements';

function ModalScreen() {
const navigation = useNavigation();
const { colors } = useTheme();
const { current } = useCardAnimation();

return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Pressable
style={[
StyleSheet.absoluteFill,
{ backgroundColor: 'rgba(0, 0, 0, 0.5)' },
]}
onPress={navigation.goBack}
/>
<Animated.View
style={{
padding: 16,
width: '90%',
maxWidth: 400,
borderRadius: 3,
backgroundColor: colors.card,
transform: [
{
scale: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0.9, 1],
extrapolate: 'clamp',
}),
},
],
}}
>
<Text>
Mise en place is a French term that literally means “put in place. It
also refers to a way cooks in professional kitchens and restaurants
set up their work stations—first by gathering all ingredients for a
recipes, partially preparing them (like measuring out and chopping),
and setting them all near each other. Setting up mise en place before
cooking is another top tip for home cooks, as it seriously helps with
organization. It’ll pretty much guarantee you never forget to add an
ingredient and save you time from running back and forth from the
pantry ten times.
</Text>
<Button
color={colors.primary}
style={{ alignSelf: 'flex-end' }}
onPress={navigation.goBack}
>
Okay
</Button>
</Animated.View>
</View>
);
}

在这里,我们动画化对话框的缩放,并添加一个叠加层以关闭对话框。