屏幕元素动画
本指南介绍如何在屏幕之间动画元素。此功能被称为共享元素过渡,它在 @react-navigation/native-stack
中使用 React Native Reanimated 实现。
警告
在编写本指南时,共享元素过渡被认为是实验性功能,不建议在生产环境中使用。
前提条件
在继续本指南之前,请确保你的应用满足以下条件
- 你正在使用
@react-navigation/native-stack
。共享元素过渡功能在基于 JS 的@react-navigation/stack
中不受支持。 - 你已安装并配置了
react-native-reanimated
v3.0.0 或更高版本。
最小示例
要创建共享过渡
- 使用从
react-native-reanimated
导入的Animated
组件。 - 将相同的
sharedTransitionTag
分配给不同屏幕上的元素。 - 在屏幕之间导航。过渡将自动开始。
- 静态
- 动态
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 列表中哪个元素是哪个。
自定义过渡
默认情况下,过渡使用 withTiming
和 500 毫秒的持续时间来动画 width
、height
、originX
、originY
和 transform
属性。你可以轻松自定义 width
、height
、originX
和 originY
属性。自定义 transform
也是可能的,但这远远超出了本指南的范围。
警告
自定义 SharedTransition API 尚未最终确定,可能会在未来的版本中更改。
要自定义过渡,你需要传递除 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
/>
);
}
参考
你可以在 React Native Reanimated 文档中找到完整的共享元素过渡参考。
替代方案
或者,你可以使用 react-native-shared-element
库及其 React Navigation 绑定,它在基于 JS 的 @react-navigation/stack
导航器中实现了共享元素过渡。然而,此解决方案未得到积极维护。
react-native-navigation
也支持共享元素过渡。你可以在此处阅读更多相关信息。