堆栈导航器
堆栈导航器提供了一种在屏幕之间转换的方式,其中每个新屏幕都放置在堆栈的顶部。
默认情况下,堆栈导航器配置为具有熟悉的 iOS 和 Android 外观和感觉:新屏幕在 iOS 上从右侧滑入,在 Android 上使用操作系统默认动画。但是,动画可以自定义以满足您的需求。
需要记住的一件事是,虽然 @react-navigation/stack
非常可定制,但它是用 JavaScript 实现的。虽然它使用原生方式运行动画和手势,但性能可能不如原生实现快。对于许多应用程序来说,这可能不是问题,但是如果您在导航期间遇到性能问题,请考虑使用 @react-navigation/native-stack
代替 - 它使用原生导航原语。
安装
要使用此导航器,请确保您已安装 @react-navigation/native
及其依赖项(请遵循本指南),然后安装 @react-navigation/stack
- npm
- Yarn
- pnpm
npm install @react-navigation/stack
yarn add @react-navigation/stack
pnpm add @react-navigation/stack
然后,您需要安装和配置堆栈导航器所需的库
-
首先,安装
react-native-gesture-handler
。如果您有 Expo 管理的项目,请在您的项目目录中运行
npx expo install react-native-gesture-handler
如果您有一个裸 React Native 项目,请在您的项目目录中运行
- npm
- Yarn
- pnpm
npm install react-native-gesture-handler
yarn add react-native-gesture-handler
pnpm add react-native-gesture-handler
-
为了完成
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.js
或App.js
)的顶部添加以下内容(确保它在顶部,并且前面没有任何其他内容)import './gesture-handler';
由于堆栈导航器在 Web 上不使用
react-native-gesture-handler
,这避免了不必要地增加捆绑包大小。警告如果您正在为 Android 或 iOS 构建,请不要跳过此步骤,否则您的应用可能会在生产环境中崩溃,即使它在开发环境中运行良好。这不适用于其他平台。
-
可选地,您还可以安装
@react-native-masked-view/masked-view
。如果您想为标题使用 UIKit 样式动画(HeaderStyleInterpolators.forUIKit
),则需要这样做。如果您有 Expo 管理的项目,请在您的项目目录中运行
npx expo install @react-native-masked-view/masked-view
如果您有一个裸 React Native 项目,请在您的项目目录中运行
- npm
- Yarn
- pnpm
npm install @react-native-masked-view/masked-view
yarn add @react-native-masked-view/masked-view
pnpm add @react-native-masked-view/masked-view
-
如果您在 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,
},
});
import { createStackNavigator } from '@react-navigation/stack';
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
);
}
API 定义
Props
除了所有导航器共享的通用 props 之外,堆栈导航器还接受以下附加 props
detachInactiveScreens
布尔值,用于指示是否应将非活动屏幕从视图层次结构中分离以节省内存。这实现了与 react-native-screens 的集成。默认为 true
。
如果您需要为特定屏幕禁用此优化(例如,您希望屏幕即使在未聚焦时也保持在视图中),请使用 detachPreviousScreen
选项。
选项
以下 选项 可用于配置导航器中的屏幕。这些可以在 Stack.navigator
的 screenOptions
prop 或 Stack.Screen
的 options
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
- 当gestureDirection
为horizontal
或horizontal-inverted
时135
- 当gestureDirection
为vertical
或vertical-inverted
时
Web 上不支持此功能。
gestureVelocityImpact
数字,它决定了手势速度的相关性。默认为 0.3。
Web 上不支持此功能。
gestureDirection
手势的方向。有关详细信息,请参阅 动画部分。
Web 上不支持此功能。
transitionSpec
屏幕过渡的配置对象。有关详细信息,请参阅 动画部分。
cardStyleInterpolator
卡片各个部分的插值样式。有关详细信息,请参阅 动画部分。
headerStyleInterpolator
标题各个部分的插值样式。有关详细信息,请参阅 动画部分。
keyboardHandlingEnabled
如果为 false
,则从该屏幕导航到新屏幕时,键盘不会自动消失。默认为 true
。
detachPreviousScreen
布尔值,用于指示是否将上一个屏幕从视图层次结构中分离以节省内存。如果您需要通过活动屏幕看到上一个屏幕,请将其设置为 false
。仅当 detachInactiveScreens
未设置为 false
时适用。
当使用 presentation
作为 transparentModal
或 modal
以保持所需屏幕可见时,这将自动调整。在其他情况下,默认为 true
。
freezeOnBlur
布尔值,指示是否阻止非活动屏幕重新渲染。默认为 false
。当在应用程序顶部运行来自 react-native-screens
包的 enableFreeze()
时,默认为 true
。
仅在 iOS 和 Android 上受支持。
标题相关选项
您可以在此处找到标题相关选项的列表。这些选项可以在 Stack.navigator
的 screenOptions
prop 或 Stack.Screen
的 options
prop 中指定。您不必直接使用 @react-navigation/elements
即可使用这些选项,它们只是在该页面中记录。
除了这些之外,堆栈还支持以下选项
header
要使用的自定义标题,而不是默认标题。
这接受一个返回 React Element 的函数以显示为标题。该函数接收一个对象,其中包含以下属性作为参数
navigation
- 当前屏幕的导航对象。route
- 当前屏幕的路由对象。options
- 当前屏幕的选项layout
- 屏幕的尺寸,包含height
和width
属性。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.current
和 progress.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
- 整个屏幕的布局。包含height
和width
属性。
insets
- 安全区域插角的布局。包含top
、right
、bottom
和left
属性。
有关如何使用此 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
- 一个对象,用于指定动画类型 (timing
或spring
) 及其选项(例如timing
的duration
)。它接受 2 个属性open
- 添加屏幕时过渡的配置close
- 删除屏幕时过渡的配置。
每个对象都应指定 2 个属性
animation
- 用于动画的动画函数。支持的值为timing
和spring
。config
- 定时函数的配置对象。对于timing
,它可以是duration
和easing
。对于spring
,它可以是stiffness
、damping
、mass
、overshootClamping
、restDisplacementThreshold
和restSpeedThreshold
。
使用弹簧动画的配置如下所示
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
- 整个屏幕的布局。包含height
和width
属性。
-
请注意,当屏幕不是最后一个屏幕时,它将使用下一个屏幕的过渡配置。 这是因为许多过渡涉及前一个屏幕的动画,因此这两个过渡需要保持在一起,以防止在两个屏幕上运行两种不同类型的过渡(例如,滑动和模态)。您可以检查
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.progress
和next.progress
,并且由于current.progress
保持在1
,而next.progress
正在变化,因此组合后,进度将从1
变为2
。屏幕B
将仅具有current.progress
,它将从0
变为1
。因此,我们可以为0-1
和1-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
- 我们用于动画的各种项目的布局测量。每个布局对象都包含height
和width
属性。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>
);
}
在这里,我们动画化对话框的缩放,并添加一个叠加层以关闭对话框。