在屏幕之间设置动画元素
本指南介绍了如何在屏幕之间制作元素动画。此功能称为 共享元素转换,并在 @react-navigation/native-stack
和 React Native 复活 中实现。
¥This guide covers how to animate elements between screens. This feature is known as a Shared Element Transition and it's implemented in the @react-navigation/native-stack
with React Native Reanimated.
在撰写本指南时,共享元素转换被视为实验性功能,不建议用于生产使用。
¥As of writing this guide, Shared Element Transitions are considered an experimental feature not recommended for production use.
先决条件
¥Pre-requisites
在继续本指南之前,请确保你的应用满足以下条件:
¥Before continuing this guide make sure your app meets these criteria:
-
你正在使用
@react-navigation/native-stack
。基于 JS 的@react-navigation/stack
不支持共享元素转换功能。¥You are using
@react-navigation/native-stack
. The Shared Element Transitions feature isn't supported in JS-based@react-navigation/stack
. -
你已安装并配置
react-native-reanimated
v3.0.0 或更高版本。¥You have
react-native-reanimated
v3.0.0 or higher installed and configured.
最小的例子
¥Minimal example
要创建共享过渡:
¥To create a shared transition:
-
使用从
react-native-reanimated
导入的Animated
组件。¥Use
Animated
components imported fromreact-native-reanimated
. -
将相同的
sharedTransitionTag
分配给不同屏幕上的元素。¥Assign the same
sharedTransitionTag
to elements on different screens. -
在屏幕之间导航。过渡将自动开始。
¥Navigate between screens. The transition will start automatically.
- Static
- Dynamic
import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import {
useNavigation,
createStaticNavigation,
} from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Button } from '@react-navigation/elements';
import Animated from 'react-native-reanimated';
function HomeScreen() {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Button onPress={() => navigation.navigate('Details')}>
Go to Details
</Button>
<Animated.Image
source={{ uri: 'https://picsum.photos/id/39/200' }}
style={{ width: 300, height: 300 }}
sharedTransitionTag="tag"
/>
</View>
);
}
function DetailsScreen() {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Button onPress={() => navigation.goBack()}>Go back</Button>
<Animated.Image
source={{ uri: 'https://picsum.photos/id/39/200' }}
style={{ width: 100, height: 100 }}
sharedTransitionTag="tag"
/>
</View>
);
}
const RootStack = createNativeStackNavigator({
screens: {
Home: HomeScreen,
Details: DetailsScreen,
},
});
const Navigation = createStaticNavigation(RootStack);
export default function App() {
return <Navigation />;
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
},
});
import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Button } from '@react-navigation/elements';
import Animated from 'react-native-reanimated';
const Stack = createNativeStackNavigator();
function HomeScreen() {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Button onPress={() => navigation.navigate('Details')}>
Go to Details
</Button>
<Animated.Image
source={{ uri: 'https://picsum.photos/id/39/200' }}
style={{ width: 300, height: 300 }}
sharedTransitionTag="tag"
/>
</View>
);
}
function DetailsScreen() {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Button onPress={() => navigation.goBack()}>Go back</Button>
<Animated.Image
source={{ uri: 'https://picsum.photos/id/39/200' }}
style={{ width: 100, height: 100 }}
sharedTransitionTag="tag"
/>
</View>
);
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
},
});
sharedTransitionTag
是一个字符串,在单个屏幕的上下文中必须是唯一的,但必须匹配屏幕之间的元素。这个属性允许 Reanimated 识别元素并为其设置动画,类似于 key
属性,它告诉 React 列表中的哪个元素是哪个。
¥sharedTransitionTag
is a string that has to be unique in the context of a single screen, but has to match elements between screens. This prop allows Reanimated to identify and animate the elements, similarly to the key
property, which tells React which element in the list is which.
自定义过渡
¥Customizing the transition
默认情况下,过渡使用 withTiming
为 width
、height
、originX
、originY
和 transform
属性设置动画,持续时间为 500 毫秒。你可以轻松自定义 width
、height
、originX
和 originY
属性。自定义 transform
也是可能的,但这远远超出了本指南的范围。
¥By default, the transition animates the width
, height
, originX
, originY
and transform
properties using withTiming
with a 500 ms duration. You can easily customize width
, height
, originX
, and originY
props. Customizing transform
is also possible but it's far beyond the scope of this guide.
自定义 SharedTransition API 尚未最终确定,可能会在未来版本中发生变化。
¥Custom SharedTransition API is not finalized and might change in a future release.
要自定义过渡,你需要传递除 transform
之外的所有属性。
¥To customize the transition you need to pass all the properties besides transform
.
import { SharedTransition } from 'react-native-reanimated';
const customTransition = SharedTransition.custom((values) => {
'worklet';
return {
height: withSpring(values.targetHeight),
width: withSpring(values.targetWidth),
originX: withSpring(values.targetOriginX),
originY: withSpring(values.targetOriginY),
};
});
function HomeScreen() {
return (
<Animated.Image
style={{ width: 300, height: 300 }}
sharedTransitionTag="tag"
sharedTransitionStyle={customTransition} // add this to both elements on both screens
/>
);
}
参考
¥Reference
你可以在 React Native Reanimated 文档 中找到完整的共享元素转换参考。
¥You can find a full Shared Element Transitions reference in the React Native Reanimated documentation.
备择方案
¥Alternatives
或者,你可以将 react-native-shared-element
库与 React 导航绑定 一起使用,后者在基于 JS 的 @react-navigation/stack
导航器中实现共享元素转换。然而,该解决方案并未得到积极维护。
¥Alternatively, you can use react-native-shared-element
library with a React Navigation binding which implements Shared Element Transitions in a JS-based @react-navigation/stack
navigator. This solution, however, isn't actively maintained.
react-native-navigation
还支持共享元素转换。你可以阅读更多相关内容 此处。
¥The react-native-navigation
also comes with support for Shared Element Transitions. You can read more about it here.