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>
Related
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
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>
I tried to use transition for opening and closing my drawer but there is a problem which is my drawer is over my screen which is not satisfying. I want my screens become over my drawer.
you can clearly see below (right one is desired and left is what I achieved so far):
I tried to use zIndex on my styles but no luck. here is my navigation code:
const IntroStack = createStackNavigator();
const IntroNavigation = () => {
return (
<NavigationContainer>
<IntroStack.Navigator>
<IntroStack.Screen name='intro' component={Intro} options={{ headerShown: false }} />
<IntroStack.Screen name='login' component={Login} options={{ headerShown: false }} />
<IntroStack.Screen name='createAccount' component={CreateAccount} options={{ headerShown: false }} />
<IntroStack.Screen name='forgotPassword' component={ForgotPassword} options={{ headerShown: false }} />
<IntroStack.Screen name='enterCode' component={EnterCode} options={{ headerShown: false }} />
<IntroStack.Screen name='changePassword' component={ChangePassword} options={{ headerShown: false }} />
<IntroStack.Screen name='home' component={DrawerNavigation} options={{ headerShown: false }} />
<IntroStack.Screen name='notfications' component={Notifications} options={{ headerShown: true }} />
</IntroStack.Navigator>
</NavigationContainer>
)
}
const Drawer = createDrawerNavigator();
const DrawerNavigation = () => {
const [progress, setProgress] = useState(new Animated.Value(0));
const rotate = Animated.interpolate(progress, {
inputRange: [0, 1],
outputRange: [0, -0.28],
});
const scale = Animated.interpolate(progress, {
inputRange: [0, 1],
outputRange: [1, 0.8],
});
const screenStyles = { transform: [{ rotate, scale }] }
return (
<Drawer.Navigator drawerType='slide' drawerStyle={{width: 300}} overlayColor='transparent' sceneContainerStyle={{backgroundColor: '#3170FF'}} drawerContent={
(props) => {
setProgress(props.progress);
return <DrawerContent {...props} />
}}>
<Drawer.Screen name="Home">
{(props) => <TabNavigation style={screenStyles} />}
</Drawer.Screen>
</Drawer.Navigator>
);
}
const Tab = createBottomTabNavigator();
const TabNavigation = (props) => {
return (
<Animated.View style={[{ flex: 1 }, props.style]}>
<Tab.Navigator tabBarOptions={{
style: {
borderTopLeftRadius: 23,
borderTopRightRadius: 23,
height: 70,
alignItems: 'center',
}
}} >
<Tab.Screen name="Home" component={Home} options={{
tabBarButton: (props) => <TabComponent label='home' {...props} />,
}} />
<Tab.Screen name="WorldTour" component={WorldTour} options={{
tabBarButton: (props) => <TabComponent label='earth' {...props} />,
}} />
<Tab.Screen name="Outlet" component={Outlet} options={{
tabBarButton: (props) => <TabComponent label='compass' {...props} />,
}} />
<Tab.Screen name="ChrisLeong" component={ChrisLeong} options={{
tabBarButton: (props) => <TabComponent label='crown' {...props} />,
}} />
<Tab.Screen name="OurTherapists" component={OurTherapists} options={{
tabBarButton: (props) => <TabComponent label='joint' {...props} />,
}} />
</Tab.Navigator>
</Animated.View>
);
}
export default IntroNavigation;
how can I fix this issue? thanks in advance!
import React from 'react';
import {View} from 'react-native';
import {useDrawerProgress} from '#react-navigation/drawer';
import Animated, {interpolate, useAnimatedStyle} from 'react-native-reanimated';
import {AppStyle} from '../../theme';
export default function DrawerScreen({children, style}) {
const drawerProgress = useDrawerProgress();
const viewStyles = useAnimatedStyle(() => {
const scale = interpolate(drawerProgress.value, [0, 1], [1, 0.75]);
const translateX = interpolate(drawerProgress.value, [0, 1], [0, 60]);
const borderRadius = interpolate(drawerProgress.value, [0, 1], [1, 10]);
const rotate = interpolate(drawerProgress.value, [0, 1], [0, -12]);
return {
transform: [
{scale},
{translateX},
{translateY: translateX - 100},
{rotate: `${rotate}deg`},
],
borderRadius,
};
});
const cardStyles = useAnimatedStyle(() => {
const translateTransparentCard = interpolate(
drawerProgress.value,
[0, 0.4, 1],
[0, 0, 0],
);
const scale = interpolate(drawerProgress.value, [0, 1], [1, 0.6]);
const rotate = interpolate(drawerProgress.value, [0, 1], [0, -18]);
return {
transform: [
{translateX: -translateTransparentCard},
{translateY: translateTransparentCard + 20},
{scale},
{rotate: `${rotate}deg`},
],
};
});
return (
<View style={AppStyle.fill}>
<Animated.View
style={[
{
opacity: 0.8,
borderRadius: 10,
overflow: 'hidden',
position: 'absolute',
},
AppStyle.fullSize,
cardStyles,
style,
]}>
{children}
</Animated.View>
<Animated.View style={[style, viewStyles]}>
<View style={[AppStyle.hideOverFlow, AppStyle.fill]}>{children}</View>
</Animated.View>
</View>
);
}
If you're using react navigation, you can use drawerType: "slide" in your drawer configuration
That is available on react-navigation above v3.x and should achieve what you're expecting.
https://reactnavigation.org/docs/drawer-navigator/#drawertype
I'm unable to move between the nested stacks.
I've traced a route from the RootStackScreen back to all the different screens I want to display.
No issues at all with push, pop or back around screens in the same stack but I'm unable to move between them when one is initially displayed.
Does useStates block me from navigation between stacks or is there something else which I'm missing?!
Thanks in advance.
import React, {useState, useEffect} from 'react';
import { NavigationContainer } from '#react-navigation/native'
import { createStackNavigator } from '#react-navigation/stack'
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs'
import Icon from 'react-native-vector-icons/Feather'
import firebase from 'firebase'
// Screens
// Modal
import Modal from '../screens/modal';
// Onboarding
import OnboardingOne from '../screens/onboardingOne'
import OnboardingTwo from '../screens/onboardingTwo'
import OnboardingThree from '../screens/onboardingThree'
import OnboardingFour from '../screens/onboardingFour'
import SignIn from '../screens/signIn'
import CreateUser from '../screens/createUser'
import OnboardingUser from '../screens/onboardingUser'
// Loading
import Loading from '../screens/loading'
// Tabs
import HomeScreen from '../screens/home'
import AboutScreen from '../screens/about'
import OrganisationScreen from '../screens/organisation'
// Profile
import ProfileScreen from '../screens/profile'
import SignOutScreen from '../screens/signOut'
const OnboardingStack = createStackNavigator();
const OnboardingStackScreen = () => (
<OnboardingStack.Navigator
headerMode='screen'
screenOptions={{animationEnabled: true}}
initialRouteName="OnboardingUser" >
<OnboardingStack.Screen
name="OnboardingOne"
component={OnboardingOne}
options={{
headerTitle: 'Onboarding One',
}}
/>
<OnboardingStack.Screen
name="OnboardingTwo"
component={OnboardingTwo}
options={{
headerTitle: 'OnboardingTwo'
}}
/>
<OnboardingStack.Screen
name="OnboardingThree"
component={OnboardingThree}
options={{
headerTitle: 'OnboardingThree'
}}
/>
<OnboardingStack.Screen
name="OnboardingFour"
component={OnboardingFour}
options={{
headerTitle: 'Onboarding Four'
}}
/>
<OnboardingStack.Screen
name="SignIn"
component={SignIn}
options={{
headerTitle: 'Sign in'
}}
/>
<OnboardingStack.Screen
name="CreateUser"
component={CreateUser}
options={{
headerTitle: 'Create User'
}}
/>
<OnboardingStack.Screen
name="OnboardingUser"
component={OnboardingUser}
options={{
headerTitle: 'Onboarding User',
}}
/>
</OnboardingStack.Navigator>
);
const ProfileStack = createStackNavigator();
const ProfileStackStackScreen = () => (
<ProfileStack.Navigator headerMode='screen' screenOptions={{animationEnabled: true}} >
<ProfileStack.Screen
name="Profile"
component={ProfileScreen}
options={{
headerTitle: 'Profile Screen',
}}
/>
<ProfileStack.Screen
name="SignOut"
component={SignOutScreen}
options={{
headerTitle: 'Sign Out',
}}
/>
</ProfileStack.Navigator>
);
const AppTabs = createBottomTabNavigator()
const AppTabsScreen = () => (
<AppTabs.Navigator
initialRouteName="Home"
tabBarOptions={{
activeTintColor: 'orange',
inactiveTintColor: 'gray',
}}>
<AppTabs.Screen name="Home" component={HomeScreen} options={{
tabBarLabel: "Hem",
tabBarIcon: props => <Icon name="home"
size={props.size}
color={props.color}/>
}}/>
<AppTabs.Screen name="About" component={AboutScreen} options={{
tabBarLabel: "Om",
tabBarIcon: props => (
<Icon name="feather"
size={props.size}
color={props.color}/>)
}}/>
<AppTabs.Screen name="Organisation" component={OrganisationScreen} options={{
tabBarLabel: "Organisationer",
tabBarIcon: props => <Icon name="list"
size={props.size}
color={props.color}/>
}}/>
<AppTabs.Screen name="Profile" component={ProfileStackStackScreen} options={{
tabBarLabel: "Profile",
tabBarIcon: props => <Icon name="user"
size={props.size}
color={props.color}/>
}}/>
</AppTabs.Navigator >
);
const RootStack = createStackNavigator();
const RootStackScreen = () => {
const [isLoading, setIsLoading] = useState(true)
const [isLoggedIn, setLoggedIn] = useState(false)
// Do we have a user?
var user = firebase.auth().currentUser;
// Set wait time for loading screen
useEffect(() => {
setIsLoading(!isLoading)
}, [])
return (
<RootStack.Navigator
headerMode="none"
screenOptions={{ animationEnabled: false }}
mode="modal">
{isLoading ? (
<RootStack.Screen name="Loading" component={Loading} />
) : !isLoggedIn ? (
<RootStack.Screen name="AppDrawerScreen" component={OnboardingStackScreen} />
) : (
<RootStack.Screen name="TabHomeScreen" component={AppTabsScreen} />
)}
<RootStack.Screen
name="Modal"
component={ Modal }
options={{ animationEnabled: true }}
/>
<RootStack.Screen
name="Alert"
component={ Modal }
options={{
animationEnabled: true,
cardStyle: { backgroundColor: 'rgba(0,0,0,0.15)'},
cardOverlayEnabled: true,
cardStyleInterpolator: ({current: { progress }}) => {
return{
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',
})
}
}
}
}}
/>
</RootStack.Navigator>
);
};
export default () => {
return (
<NavigationContainer>
<RootStackScreen/>
</NavigationContainer>
)
}
Apparently useState (isLoading & isLoggedIn) prevents me from navigating between these components. Simply fixed my problem by adding:
<RootStack.Screen name="BottomTabNavigation" component={AppTabsScreen}/>
Placed under the the hooks that determine the initial screen of the app and now everything works as intended and navigation.navigate("BottomTabNavigation") now displays the bottomTabNavigator.
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