我想我得到了本文https://medium.com/async-la/custom-transitions-in-react-navigation-2f759408a053 中描述的解决方案,它解释了导航的工作原理(强烈建议阅读它,因为它的阅读速度非常快,并帮助我更好地理解了转换的工作原理)。
这是一个示例导航器的 gif 图像,该导航器具有平滑的过渡,可以来回移动多个屏幕:
Navigator Gif
(对不起,这是一个链接,我还没有足够的代表来嵌入视频,所以我不得不做一个链接)。但是,这显示了源代码在下面的导航器的行为。
基本上发生的情况是,如果您想从堆栈上的屏幕 4 转到 1,您可以将 2 和 3 的不透明度设置为 0,这样过渡看起来会从 4 转到 1。
这是导航器的源代码:
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import ScreenA from './ScreenA';
import ScreenB from './ScreenB';
import ScreenC from './ScreenC';
import ScreenD from './ScreenD';
const OuterNavigator = createStackNavigator(
{
ScreenA: {
screen: ScreenA,
navigationOptions: {
//Set header stuff here
headerTintColor: "#fff",
headerStyle: {
backgroundColor: '#4444aa',
},
headerTitle: "Screen A"
}
},
ScreenB: {
screen: ScreenB,
navigationOptions: {
headerTintColor: "#fff",
headerStyle: {
backgroundColor: '#44aa44',
},
headerTitle: "Screen B"
}
},
ScreenC: {
screen: ScreenC,
navigationOptions: {
headerTintColor: "#fff",
headerStyle: {
backgroundColor: '#aa4444',
},
headerTitle: "Screen C"
}
},
ScreenD: {
screen: ScreenD,
navigationOptions: {
headerTintColor: "#fff",
headerStyle: {
backgroundColor: '#44aaaa',
},
headerTitle: "Screen D"
}
},
},
{
// Sets initial screen to screen A
initialRouteName: 'ScreenA',
// Can be changed to whatever you prefer
headerMode: 'float',
// This line makes a transparent background so the navigator can be wrapped in a background image
// (this was an issue I struggled with and figured there's a change someone else might be as well)
cardStyle: { backgroundColor: '00000000', shadowColor: '000000' },
transitionConfig: () => ({
// This link exlpains this in detail:
// https://medium.com/async-la/custom-transitions-in-react-navigation-2f759408a053
containerStyle: {
// This also has to do with having a background image
backgroundColor: '00000000',
},
transitionSpec: {
// Sets speed for transition (lower -> faster)
duration: 500,
useNativeDriver: true,
},
screenInterpolator: sceneProps => {
// Parses scene props
const { position, layout, scene, index, scenes } = sceneProps
// Grabs height and width of screen
const toIndex = index
const thisSceneIndex = scene.index
const height = layout.initHeight
const width = layout.initWidth
// Grab index of last screen
const lastSceneIndex = scenes[scenes.length - 1].index
// Calculates change in indices
const deltaScene = (lastSceneIndex - toIndex == 0) ? 1 : (lastSceneIndex - toIndex);
let translateX = position.interpolate({
inputRange: [thisSceneIndex - 1, thisSceneIndex],
// Adjusts how the output get scaled
outputRange: [width / deltaScene, 0],
});
const translateY = position.interpolate({
// Not used, but in the link they use this for vertical transitions
// If you're interested in that this would do it
inputRange: [0, thisSceneIndex],
outputRange: [height, 0]
});
// MAGIC HAPPENS HERE:
// Test whether we're skipping back more than one screen
// and slide from bottom if true
if (lastSceneIndex - toIndex > 1) {
// If you want the screen to which you are transitioning to not move
// have return; in this if block (see link). If you want behaviour as
// shown in the GIF then leave this
if (scene.index === toIndex) {
return { transform: [{ translateX }] }
}
// BIG MAGIC HERE
// Hide all screens in between
if (scene.index !== lastSceneIndex) return { opacity: 0 }
}
return { transform: [{ translateX }] }
},
}),
}
);
因此,例如,假设您的堆栈类似于 [A, B, C, D](因此 D 在顶部)。 lastSceneIndex 获取最后一个场景的索引(在本例中为 D)。 DeltaScene 计算场景变化的程度。 DeltaScene 只会在 != 1 时返回超过 1 个屏幕。如果您不熟悉 translateX 的工作原理,我强烈建议您阅读上述链接;输出范围为 [width/deltaScene, 0] 的原因是在动画开始之前,屏幕相互堆叠,然后动画比例将它们展开成 [A][B][C][D ] 其中每个屏幕的宽度/deltaScene 距离。在本例中,deltaScene 为 3,因此 B 为 A 右侧的宽度/3,C 为 A 右侧的 2 * 宽度/3,D 为 A 右侧的 3 * 宽度/3 = 宽度,即是我们想要的。如果没有 deltaScene,D 向右飞的速度比 A 出现在屏幕上的速度快 3 倍,这会造成丑陋的过渡。
另外,我怀疑任何人都需要看到这一点,但以防万一,这是它在 App.js 中的使用方式(提供程序用于 redux,因此如果您不想处理它,可以省略它)
<Provider store={store}>
<ImageBackground source={require('./assets/background.png')} style={styles.backgroundImage} resizeMode='cover'>
<AppNavigator />
</ImageBackground>
</Provider>
...
const styles = StyleSheet.create({
backgroundImage: {
flex: 1,
}
});
我真的希望这会有所帮助!
我对本机反应还是很陌生,所以如果我犯了任何错误或有任何进一步改进或解释的空间,请在 cmets 中为我/任何可能看到此内容的人说出来!