Custom animation for React Native Stack navigation - react-native

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>

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 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>

React native navigation slide from top

I want to create navigating animation whether my new screen slide from top to bottom. This code works like slide from bottom to top, I don't understand how to change it.
const SlideFromTop = (props: StackCardInterpolationProps): StackCardInterpolatedStyle => {
const progress = Animated.add(
props.current.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'clamp',
}),
props.next
? props.next.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'clamp',
})
: 0
);
return {
cardStyle: {
transform: [
{
translateY: Animated.multiply(
progress.interpolate({
inputRange: [0, 1, 2],
outputRange: [
props.layouts.screen.height, // Focused, but offscreen in the beginning
0, // Fully focused
props.layouts.screen.height * -0.3, // Fully unfocused
],
extrapolate: 'clamp',
}),
props.inverted
),
},
],
},
};
};
My navigation looks like
<NavigationContainer>
<Stack.Navigator
initialRouteName="List"
headerMode="none">
<Stack.Screen
name="Calendar"
component={CalendarSlider}
options={{
title: 'CalendarSlider',
cardStyleInterpolator: SlideFromTop,
}}
/>
</Stack.Navigator>
</NavigationContainer>
The issue seems to be with the outputRange of your interpolator - you probably want to specify -props.layouts.screen.height as the start- and endpoint, so that to position the screen top off screen:
outputRange: [
-props.layouts.screen.height,
0,
-props.layouts.screen.height,
]

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

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