react native - change navigation direction (i.e, from right to left) - react-native

I am using react-navigation to navigate from one screen to another.
By the way I am using createStackNavigator.
I am using the code below to navigate between screens.
<Button onPress={()=>this.props.navigation.navigate('ScreenTwo')}>button-></Button>
It works fine, but I want to change the animation direction. Currently when I press the button the ScreenTwo just pops up, instead I want the screen to slide from right to left.
Is the a way I could change the direction of the animation when navigating?

Answered by satya164 in react-navigation/stack github repo, using gestureDirection: 'horizontal-inverted' in defaultNavigationOptions/navigationOptions
Screen: {
screen: Screen,
navigationOptions: {
...TransitionPresets.SlideFromRightIOS,
gestureDirection: 'horizontal-inverted',
},
},
related links below:
https://github.com/react-navigation/stack/issues/377#issuecomment-578504696
https://reactnavigation.org/docs/stack-navigator/#animation-related-options

You need to use Custom Screen Transitions in side your navigation configurations. Try following code, (make sure to import Easing, Animated from 'react-native')
const yourStack = createStackNavigator(
{
One: ScreenOne,
Two: DetailsTwo,
},
{
initialRouteName: 'One',
transitionConfig: () => ({
transitionSpec: {
duration: 300,
easing: Easing.out(Easing.poly(4)),
timing: Animated.timing,
},
screenInterpolator: sceneProps => {
const {layout, position, scene} = sceneProps;
const {index} = scene;
const width = layout.initWidth;
const translateX = position.interpolate({
inputRange: [index - 1, index, index + 1],
outputRange: [width, 0, 0],
});
const opacity = position.interpolate({
inputRange: [index - 1, index - 0.99, index],
outputRange: [0, 1, 1],
});
return {opacity, transform: [{translateX: translateX}]};
},
})
}
);

For me this worked well with "react-native": "0.62.2","react-navigation": "4.4.4", "react-navigation-stack": "2.10.4",:
import {createStackNavigator, CardStyleInterpolators} from '#react-navigation/stack';
const RootStack = createStackNavigator();
function Root(props) {
return (
<RootStack.Navigator headerMode="none" mode="modal">
<RootStack.Screen
name="NextScreen"
options={{
gestureDirection: 'horizontal',
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
}}
component={NextScreenComponent}
/>
</RootStack.Navigator>
)}

For version 4.x.x -
import {
createStackNavigator,
CardStyleInterpolators,
} from 'react-navigation-stack';
const CatalogStack = createStackNavigator(
{
Catalog: Catalog,
ProductDetails: ProductDetails,
EditProduct: EditProduct,
Categories: Categories,
SubCategories: SubCategories,
ChooseColors: ChooseColors,
ChooseSizes: ChooseSizes,
},
{
defaultNavigationOptions: {
headerShown: false,
gestureEnabled: false,
swipeEnabled: false,
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
},
},
);
For 5.x.x -
import {
createStackNavigator,
CardStyleInterpolators,
} from '#react-navigation/stack';
<HomeStack.Navigator
initialRouteName="Home"
headerMode="none"
screenOptions={{
gestureEnabled: false,
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
}}>
<HomeStack.Screen name="Home" component={Home} />
</HomeStack.Navigator>

This worked for me:
<AppStack.Navigator headerMode="none" initialRouteName="Home">
<AppStack.Screen
name="LeftMenu"
component={LeftMenu}
options={{ gestureDirection: "horizontal-inverted" }}
/>
</AppStack.Navigator>

// some import
import { Animated, Easing } from 'react-native'
import { createStackNavigator } from '#react-navigation/stack';
const Stack = createStackNavigator();
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
),
},
],
},
};
};
const config = {
duration: 300,
easing: Easing.out(Easing.poly(4)),
timing: Animated.timing,
};
const SettingsNavigation = () => (
<Stack.Navigator screenOptions={{ tabBarVisible: false }}>
<Stack.Screen name="Tags" component={TagsScreen} options={{ headerShown: false, transitionSpec: { open: config, close: config }, cardStyleInterpolator: forSlide}} />
</Stack.Navigator>
);
I found this solution on https://reactnavigation.org/docs/stack-navigator/#animations

Related

Partially transparent modal overlay screen with previous screen in the background in react navigation 5 or 6?

We are currently using react-navigation (5.6) in our react native app.
We use a BottomTabNavigator as the root navigation.
const BottomTab = createBottomTabNavigator()
The individual screens under this BottomTab.Navigator are navigators themselves. e.g.
<BottomTab.Screen
name={AppRoute.MORE}
component={MoreTabNavigator}
options={{
title: 'More',
tabBarIcon: ({ color, size }) => {
return <Icon style={{ width: size, height: size }} fill={color} name="more-horizontal-outline" />
}
}}
/>
The MoreTabNavigator is as follows:
const MoreTabNavigator = (): React.ReactElement => {
const { Navigator, Screen } = createStackNavigator<RootStackParamList>();
return (
<Navigator headerMode="none"
screenOptions={{
headerShown: false,
cardStyle: { backgroundColor: 'transparent' },
cardOverlayEnabled: true,
cardStyleInterpolator: ({ current: { progress } }) => ({
cardStyle: {
opacity: progress.interpolate({
inputRange: [0, 0.5, 0.9, 1],
outputRange: [0, 0.25, 0.7, 1],
}),
},
overlayStyle: {
opacity: progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 0.5],
extrapolate: 'clamp',
}),
},
}),
}}
mode="modal">
<Screen
name={AppRoute.MORE}
component={MoreScreen}
/>
<Screen
name={AppRoute.CONTACT_US}
component={ContactUsScreen}
/>
</Navigator>
)
}
What I am trying to achieve is that when user clicks More tab, it should show a transparent overlay with the previous screen in the background. I want to create something like this
I am not sure how this is to be done. I have tried different solutions given on SO but haven't been able to achieve the desired effect. I always get a solid color grey background.
I tried with version 6 as well using presentationalModal however no luck. I am looking for react-navigation v5 or v6 solution
Any help or pointers would be really helpful.
Thanks in advance

Custom Drawer animation won't work after updating drawer navigation 6

i need some help about my custom drawer .
it used to work perfectly with slide animation but after updating to drawer v6.
the package react-native-reanimated has been updated too.
he stops work. can you help me guys. thanks
const CustomDrawer = ({navigation}) => {
const [progress, setProgress] = React.useState(new Animated.Value(0));
const scale = Animated.interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [1, 0.8],
});
const borderRadius = Animated.interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [0, 26],
});
const animatedStyle = {borderRadius, transform: [{scale}]};
return (
<View style={{flex: 1, backgroundColor: COLORS.primary}}>
<Drawer.Navigator
screenOptions={{
headerShown: false,
sceneContainerStyle: {backgroundColor: 'transparent'},
drawerType: 'slide',
drawerStyle: {
flex: 1,
width: '65%',
paddingRight: 20,
backgroundColor: 'transparent',
},
}}
drawerContent={props => {
// setTimeout(() => {
setProgress(props.progress);
// }, 0);
return <CustomContentDrawer navigation={props.navigation} />;
}}>
<Drawer.Screen name="MainLayout">
{props => (
<MainLayout
{...props}
drawerAnimationStyle={animatedStyle}
navigation={navigation}
/>
)}
</Drawer.Screen>
</Drawer.Navigator>
</View>
);
};
const MainLayout = ({drawerAnimationStyle, navigation}) => {
return (
<Animated.View
style={{flex: 1, backgroundColor: 'white', ...drawerAnimationStyle}}>
<Text>MainLayout</Text>
</Animated.View>
);
};
With the latest update progress prop of drawerContent seems to return undefined. So the animation is not working.
Instead of using useState , we can use useDrawerProgress() hook in the Main component instead of Drawer component. All the animation logic needs to be implemented in Main Component.
//Main Component
const MainLayout = (props) => {
const progress = useDrawerProgress();
const scale = Animated.interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [1, 0.8],
});
const borderRadius = Animated.interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [0, 26],
});
const animatedStyle = {
borderRadius,
transform: [{ scale }],
};
return (
<Animated.View
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
backgroundColor: "white",
...animatedStyle,
}}
>
<Text>MainLayout</Text>
</Animated.View>
);
};
export default MainLayout;
PS: remove any animation logic and props passed in drawer component. We don't need them anymore.
useDrawerProgress - working
try this code:
const drawerProgress = useDrawerProgress();
const animatedStyle = useAnimatedStyle(() => {
const scale = interpolate(drawerProgress.value, [0, 1], [1, 0.8], {
extrapolateRight: Extrapolate.CLAMP,
});
const borderRadius = interpolate(drawerProgress.value, [0, 1], [0, 10], {
extrapolateRight: Extrapolate.CLAMP,
});
return {
transform: [{scale}],
borderRadius,
};
});
But write this code inside screens not in the DrawerContent, for me its working!!

React-Native | drawerContent props.progress return undefined

I'm trying to buid a react-native application. I followed this tutorial in order to make the drawer part : https://www.youtube.com/watch?v=NV48FIIWaN0&t=1331s&ab_channel=ByProgrammers .
In this tutorial, he is using props.progress to update the progress status in order to compute the animation. I have nearly the same code and mine isn't working. props.progress return undefined.
Please find here my code :
const DrawerNavigator = ({route}) =>{
const [progress, setProgress] = React.useState(new Animated.Value(0))
const scale = Animated.interpolateNode(progress,{
inputRange : [0,1],
outputRange : [0,0.8]
})
const borderRadius = Animated.interpolateNode(progress,{
inputRange : [0,1],
outputRange : [0,25]
})
const animatedStyle = {transform:[{scale}]}
return(
<View style = {styles.mainContainer}>
<Drawer.Navigator
initialRouteName = "FableReader"
screenOptions={{
headerShown: false,
drawerStyle : [styles.drawerContainer],
drawerType : "slide",
overlayColor : "transparent",
sceneContainerStyle : [styles.sceneContainer],
}}
drawerContent = {({...props})=>{
setTimeout(()=>{
setProgress(props.progress)
},0)
return(
<CustomContent {...props} route = {route}></CustomContent>
)
}}
>
<Drawer.Screen name="FableReader">{props => <FableReaderScreen {...props}/>}</Drawer.Screen>
<Drawer.Screen name="LexiqueFable">{props => <LexiqueScreen {...props}/>}</Drawer.Screen>
<Drawer.Screen name="QCM">{props => <QCMScreen {...props}/>}</Drawer.Screen>
</Drawer.Navigator>
</View>
If you know what is wrong you're my hero !
Thanks again ;)
DrawerContent props.progress worked in React navigation 5. It has been removed in React navigation 6.
In React navigation 6 you can use useDrawerProgress. By default it returns 0 or 1. To get animation use useLegacyImplementation:
<Drawer.Navigator
screenOptions={{
drawerPosition: 'left',
drawerType: 'slide',
headerShown: false,
overlayColor: 'transparent',
drawerStyle: {
flex: 1,
width: '75%',
backgroundColor: 'transparent'
},
sceneContainerStyle: {
backgroundColor: 'transparent'
},
}}
initialRouteName="Home"
drawerContent={props => <Sidebar {...props} />}
useLegacyImplementation
>
<Drawer.Screen name="Home" component={Home} />
</Drawer.Navigator>
import React from "react"
import { StyleSheet } from 'react-native'
/* Animation */
import Animated from 'react-native-reanimated'
/* Drawer */
import { useDrawerProgress } from "#react-navigation/drawer"
const Home = props => {
const progress = useDrawerProgress()
const scale = Animated.interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [1, 0.8],
extrapolate: 'clamp'
})
const borderRadius = Animated.interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [0, 30],
extrapolate: 'clamp'
})
const style = { borderRadius, transform: [{ scale }] }
return (
<Animated.View style={[style, styles.box]}>
{/* Your content */}
</Animated.View>
)
}
export default Home
const styles = StyleSheet.create({
...
})
First of all, your animation-related code needs to be inside your CustomContent component, not inside DrawerNavigator.
Then remove this:
const [progress, setProgress] = React.useState(new Animated.Value(0))
And replace it with:
const progress = useDrawerProgress();
Where you import useDrawerProgress from #react-navigation/drawer.
You should put it inside your Layout component for the navigator, FableReader. And also, you should use hook -> useDrawerProgress
import Animated from 'react-native-reanimated';
import React from 'react';
import { useDrawerProgress } from '#react-navigation/drawer';
import { View, Text } from 'react-native';
const FableReader= () => {
const progress = useDrawerProgress();
const scale = Animated.interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [1, 0.8],
});
const borderRadius = Animated.interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [0, 25],
});
const animatedStyle = {
borderRadius,
transform: [{ scale }],
overflow: 'hidden',
};
return (
<Animated.View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'white',
...animatedStyle,
}}
>
<Text>FableReader page</Text>
</Animated.View>
);
};
export default FableReader;

React Native Navigation Animation slide implementation

Is it possible to make a Navigation animation between screen like in the video ?
Closest behaviour can be achieved with.
import { TransitionPresets } from '#react-navigation/stack';
<Stack.Screen
key={screen.key}
name={screen.key}
component={screen.component}
options={{
headerShown: false,
...TransitionPresets.SlideFromRightIOS,
}}
/>
You can try that:
const SlideTransition = {
cardStyleInterpolator: ({ current, next, layouts }) => {
return {
cardStyle: {
transform: [
{
translateX: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [layouts.screen.width, 0],
}),
},
{
translateX: next
? next.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, -layouts.screen.width],
})
: 1,
},
],
},
};
},
};
and then
<NavigationContainer>
<Stack.Navigator
initialRouteName={"Page1"}
screenOptions={{ headerShown: false }}
>
<Stack.Screen
name={"Page1"}
component={Page1}
options={{ ...SlideTransition }}
/>
<Stack.Screen
name={"Page2"}
component={Page2}
options={{ ...SlideTransition }}
/>
</Stack.Navigator>
</NavigationContainer>

Custom animation for React Native Stack navigation

I need to create a custom animation effect, which is pretty similar with the default one on iOS, but with a small difference: When I change the screen, I need the next screen to push the current screen to the left. The default animation is placing the next screen over the current one and it also moves the current screen to the left. But I need the next screen to literally push the current screen because I have a wave which starts from the first screen and continues to the second screen and I would like to achieve a continuation effect when changing the screens.
This is what I have for now, but it is just hiding the current screen on screen change:
<Stack.Navigator
initialRouteName="Language"
screenOptions={{
cardOverlayEnabled: true,
gestureEnabled: true,
...MyTransition,
}}>
...
</Stack.Navigator>
const MyTransition = {
gestureDirection: 'horizontal',
transitionSpec: {
open: TransitionSpecs.TransitionIOSSpec,
close: TransitionSpecs.TransitionIOSSpec,
},
cardStyleInterpolator: ({ current, next, layouts }) => {
return {
cardStyle: {
transform: [
{
translateX: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [layouts.screen.width, 0],
}),
},
],
},
};
},
};
Thanks for your help!
I just coming across this issue and i think i find the solution on react navigation official site.
const cardStyleInterpolator = ({
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
//I changed this line only
//screen.width * -0.3 // Fully unfocused
-screen.width
],
extrapolate: "clamp"
}),
inverted
)
}
]
}
};
};
https://reactnavigation.org/docs/stack-navigator
Usage:
<Stack.Navigator
screenOptions={{
headerShown: false, //Optional
cardStyle: { backgroundColor: "transparent" }, //Optional
cardStyleInterpolator
}}
>
<Stack.Screen name="ScreenA" component={ScreenA} />
<Stack.Screen name="ScreenB" component={ScreenB} />
</Stack.Navigator>