Javascript 反应导航:使用NavigationActions.reset、goBack和getStateForAction导航回根目录
假设我在StackNavigator应用程序中浏览了4个屏幕,现在我想返回到第一个屏幕。似乎有三种不同的方法可以做到这一点,它们确实可以导航到我想做的地方,但是每种方法都有一个动画循环通过前面的每个屏幕 是否有一种从Javascript 反应导航:使用NavigationActions.reset、goBack和getStateForAction导航回根目录,javascript,reactjs,react-native,react-navigation,Javascript,Reactjs,React Native,React Navigation,假设我在StackNavigator应用程序中浏览了4个屏幕,现在我想返回到第一个屏幕。似乎有三种不同的方法可以做到这一点,它们确实可以导航到我想做的地方,但是每种方法都有一个动画循环通过前面的每个屏幕 是否有一种从SCREEN\u D导航到SCREEN\u a的干净方法 换句话说,当从SCREEN\u D navigation.navigate(SCREEN_A); ... navigation.navigate(SCREEN_B); ... navigation.navigate(SCREE
SCREEN\u D
导航到SCREEN\u a
的干净方法
换句话说,当从SCREEN\u D
navigation.navigate(SCREEN_A);
...
navigation.navigate(SCREEN_B);
...
navigation.navigate(SCREEN_C);
...
navigation.navigate(SCREEN_D);
有三种方法可以做到这一点:
一,
二,
三,
这里有一个快速解决方案。
这将在导航(向前或向后)时删除所有转换
在transitionConfig中添加以下内容:
const transitionConfig = () => ({
transitionSpec: {
duration: 0,
timing: Animated.timing,
easing: Easing.step0,
},
})
transitionConfig是一个返回覆盖默认屏幕转换的对象的函数
- 如果有人知道一种在单一导航上改变动画的方法,我很想知道怎么做李>
CardStackStyleInterpolator
所提到的问题似乎被合并了。因此,您可以通过此导入加载CardStackStyleInterpolator
:
import CardStackStyleInterpolator from 'react-navigation/src/views/CardStack/CardStackStyleInterpolator'
要这样应用它:
const YourStackNavigator = StackNavigator({
Screen1: { screen: Screen1 },
Screen2: { screen: Screen2 },
}, {
transitionConfig: () => ({
screenInterpolator: (props) => CardStackStyleInterpolator.forHorizontal(props)
})
});
在我的例子中,我只是跳转到下一个屏幕,如下所示:
this.props.navigation.navigate('Modal_AddGoodClass');
但在我的reducer中,我在触发Modal_AddGoodClass
屏幕时重置了导航器:
const NewExportReceiptNavigationReducer = (state, action) => {
// Default stuff
let newStateBuffer = newState || state;
if (action) {
if (action.type === 'Navigation/NAVIGATE') {
if (action.routeName === 'Modal_AddGoodClass') {
newStateBuffer = {
index: 0,
routes: [
newStateBuffer.routes[newStateBuffer.routes.length - 1]
]
};
}
}
}
return newStateBuffer;
};
module.exports = NewExportReceiptNavigationReducer;
这非常有效,除了使用“后退”动画而不是“前进”动画之外
您还可以找到一些使用CardStackStyleInterpolator
的示例代码
2) 覆盖getStateForAction
:
如上所述,您可以覆盖路由器的getStateForAction
,以避免导航器返回。除了iOS上的“刷回”手势外,这似乎还起作用:
Nav = StackNavigator(navScreens, navOptions);
const defaultGetStateForAction = Nav.router.getStateForAction;
Nav.router.getStateForAction = (action, state) => {
if (
state &&
action.type === NavigationActions.BACK &&
(
state.routes[state.index].routeName === 'Login' ||
state.routes[state.index].routeName === 'Main'
)
) {
// Returning null indicates stack end, and triggers exit
return null;
}
return defaultGetStateForAction(action, state);
};
这是我的工作解决方案,用于在不创建新路由的情况下重置回主(根目录)
if(this.categoriesNav.state.nav.index >0){
let k = this.categoriesNav.state.nav.routes[1].key;
this.categoriesNav.dispatch(NavigationActions.back({key: k})); }
categoriesNav
引用到我的堆栈导航器您可以使用。此外,如果您使用的是redux,则应该更新您的
希望这有帮助 react导航有
poptoop
,我们可以像blow一样使用它
this.props.navigation.popToTop()
我想我找到了一个本文中描述的解决方案,它解释了导航是如何工作的(我强烈建议阅读它,因为它是一个非常快速的阅读,并帮助我更好地理解转换是如何工作的) 下面是一个示例导航器的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当一个返回到多个屏幕时。如果您不熟悉translateX的工作原理,我强烈建议您阅读上述链接;输出范围为[width/deltaScene,0]的原因是在动画开始之前,屏幕堆叠在一起,然后动画比例将像[A][B][C][D]一样展开,其中每个屏幕都是宽度/deltaScene。在这个例子中,deltaScene是3,所以B是A右边的宽度/3,C是A右边的2*width/3,D是A右边的3*width/3=宽度,这就是我们想要的。如果没有deltaScene,D向右飞的速度比A在屏幕上的速度快3倍,这会造成一个丑陋的过渡
此外,我怀疑是否有人需要看到这一点,但为了以防万一,这里介绍了它在App.js中的使用方式(提供程序用于redux,因此,如果您不想处理这一问题,可能可以省略)
...
const styles=StyleSheet.create({
背景图片:{
弹性:1,
}
});
我真的希望这有帮助!
我还是一个新手,不会做出土生土长的反应,所以如果我犯了任何错误,或者有进一步改进或解释的余地,请在评论中对我/任何其他可能看到这一点的人说出来 对于2020/react导航堆栈,我在设置StackNavigator时使用以下代码:
import { createStackNavigator, CardStyleInterpolators } from 'react-navigation-stack';
import { Easing } from 'react-native';
const Navigator = createStackNavigator({
// ...
}, {
defaultNavigationOptions: ({ navigation }) => {
const animation = navigation.getParam('_animation');
return {
// ...
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
...(animation === 'back' && {
gestureDirection: 'horizontal-inverted',
}),
...(animation === 'skip' && {
transitionSpec: {
open: { animation: 'timing', config: { duration: 0, easing: Easing.step1 }, },
close: { animation: 'timing', config: { duration: 0, easing: Easing.step0 }, },
},
}),
};
},
});
和_animation参数以覆盖动画样式
// replace to a route not in the stack normally creates a forward animation, but
// we want a backwards animation
this.props.navigation.dispatch(
StackActions.replace({ routeName: 'Login', params: { _animation: 'back' } })
);
您是否尝试过Navigator.Disclose()?我在文件里没看到。你有链接吗@这里有一个请求:@CristianoSantos真棒!这是我最好的答案!非常感谢!这会从堆栈中卸载所有屏幕吗?根据官方消息:
它跳回堆栈中的顶部路径,忽略所有其他屏幕
,感谢您的提示。我做了更简单的版本:defaultNavigationOptions:({navigation})=>{
..const skipAnimation=navigation.getParam('skipAnimation')??false
..animationEnabled:!skipAnimation,
。请参阅
this.props.navigation.popToTop()
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 }] }
},
}),
}
);
<Provider store={store}>
<ImageBackground source={require('./assets/background.png')} style={styles.backgroundImage} resizeMode='cover'>
<AppNavigator />
</ImageBackground>
</Provider>
...
const styles = StyleSheet.create({
backgroundImage: {
flex: 1,
}
});
import { createStackNavigator, CardStyleInterpolators } from 'react-navigation-stack';
import { Easing } from 'react-native';
const Navigator = createStackNavigator({
// ...
}, {
defaultNavigationOptions: ({ navigation }) => {
const animation = navigation.getParam('_animation');
return {
// ...
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
...(animation === 'back' && {
gestureDirection: 'horizontal-inverted',
}),
...(animation === 'skip' && {
transitionSpec: {
open: { animation: 'timing', config: { duration: 0, easing: Easing.step1 }, },
close: { animation: 'timing', config: { duration: 0, easing: Easing.step0 }, },
},
}),
};
},
});
// replace to a route not in the stack normally creates a forward animation, but
// we want a backwards animation
this.props.navigation.dispatch(
StackActions.replace({ routeName: 'Login', params: { _animation: 'back' } })
);