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!!
Related
I have a Floating Action Button with two internal buttons, the idea is that after clicking on the first FAB (Floating Action Button) the others are displayed, what I require is that I can click on one of the buttons that was displayed and send me to the login or to another tab, page, route.
The error I have is that I don't know where I can do the "const navigation = useNavigation()" to use the navigator and send the user to another tab
i tried to use this.props, but i couldn't get it
export default class FloatingButton extends React.Component {
handleSignOut = () => {
Alert.alert("Position 1")
authentication
.signOut()
.then(() => {
this.props.navigation.navigate("Login")
})
.catch(error => alert(error.message))
}
handleSignOut2 = () =>{Alert.alert("Position 2")}
animation = new Animated.Value(0);
toggleMenu = () => {
const toValue = this.open ? 0 : 1;
Animated.spring(this.animation, {
toValue,
friction: 5,
useNativeDriver: false
}).start();
this.open = !this.open;
};
render() {
const pinStyle2 = {
transform: [
{ scale: this.animation },
{
translateY: this.animation.interpolate({
inputRange: [0, 1],
outputRange: [0, -10]
})
}
]
};
const pinStyle = {
transform: [
{ scale: this.animation },
{
translateY: this.animation.interpolate({
inputRange: [0, 1],
outputRange: [0, -20]
})
}
]
};
const rotation = {
transform: [
{
rotate: this.animation.interpolate({
inputRange: [0, 1],
outputRange: ["0deg", "45deg"]
})
}
]
};
const opacity = this.animation.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [0, 0, 1]
})
return (
<View style={[styles.container, this.props.style]}>
<TouchableWithoutFeedback onPress={this.handleSignOut}>
<Animated.View style={[styles.button, styles.secondary, styles.menu, pinStyle, opacity]}>
<Entypo name="add-to-list" size={24} color="white" />
</Animated.View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={this.handleSignOut2}>
<Animated.View style={[styles.button, styles.secondary, styles.menu, pinStyle2, opacity]}>
<Entypo name="check" size={24} color="white" />
</Animated.View>
</TouchableWithoutFeedback>
<TouchableWithoutFeedback onPress={this.toggleMenu}>
<Animated.View style={[styles.button, styles.menu, rotation]}>
<AntDesign name="plus" size={24} color="white" />
</Animated.View>
</TouchableWithoutFeedback>
</View>
);
}
According to the react documentation "You can’t use Hooks inside a class component". (https://reactjs.org/docs/hooks-faq.html). And thats what you're doing useNavigation is a hook and you're calling it inside a class. So either
- Change your class into a function
or
- Find a way to express the same functionality as the useNavigation hook but in a class
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;
I am creating pagination dots to show the flatlist content count but the issue is that translation is not smooth here is my code for the dots.
Dots.js
import React from 'react';
import {
Animated,
StyleSheet,
useWindowDimensions,
View,
ViewStyle,
} from 'react-native';
import { colors } from '../../global_constants/colors';
export interface ScalingDotProps {
data: Array<Object>;
scrollX: Animated.Value;
containerStyle?: ViewStyle;
dotStyle?: ViewStyle;
inActiveDotOpacity?: number;
inActiveDotColor?: string;
activeDotScale?: number;
activeDotColor?: string;
}
export const ScalingDot = ({
scrollX,
data,
dotStyle,
containerStyle,
inActiveDotOpacity,
inActiveDotColor,
activeDotScale,
activeDotColor,
}: ScalingDotProps) => {
const { width } = useWindowDimensions();
const defaultProps = {
inActiveDotColor: inActiveDotColor || '#347af0',
activeDotColor: activeDotColor || '#347af0',
animationType: 'scale',
inActiveDotOpacity: inActiveDotOpacity || 0.5,
activeDotScale: activeDotScale || 1.4,
};
return (
<View style={[styles.containerStyle, containerStyle]}>
{data.map((_, index) => {
const inputRange = [
(index - 1) * width,
index * width,
(index + 1) * width,
];
const colour = scrollX.interpolate({
inputRange,
outputRange: [
defaultProps.inActiveDotColor,
defaultProps.activeDotColor,
defaultProps.inActiveDotColor,
],
extrapolate: 'clamp',
});
const opacity = scrollX.interpolate({
inputRange,
outputRange: [
defaultProps.inActiveDotOpacity,
1,
defaultProps.inActiveDotOpacity,
],
extrapolate: 'clamp',
});
const scale = scrollX.interpolate({
inputRange: inputRange,
outputRange: [1, defaultProps.activeDotScale, 1],
extrapolate: 'clamp',
});
return (
<Animated.View
key={`dot-${index}`}
style={[
styles.dotStyle,
{ opacity },
{ transform: [{ scale }] },
dotStyle,
{backgroundColor: colors.primary}
// { backgroundColor: colour },
]}
/>
);
})}
</View>
);
};
const styles = StyleSheet.create({
containerStyle: {
position: 'absolute',
bottom: 20,
paddingBottom: 2,
flexDirection: 'row',
alignSelf: 'center',
},
dotStyle: {
width: 5,
height: 5,
borderRadius: 5/2,
marginHorizontal: 5,
},
});
I want animation to work on 60FPS, I used flatlist to render the data and enabled pagination.
using the scrollX value of flatlist to change the dots active status.
Solution is:
useNativeDriver: true
I have a custom tabbar component and I want to add a button with animation and I want to make reusable but I can't figured out. For example I want to use it for 1,2,3... etc icons. I don't want to decide it in the component itself.The code is like below.
export const TabAddButton = () => {
const windowWidth = useWindowDimensions().width;
const buttonSize = useRef(new Animated.Value(1)).current;
const mode = useRef(new Animated.Value(0)).current;
const handlePress = () => {
Animated.sequence([
Animated.timing(buttonSize, {
toValue: 0.95,
duration: 5,
useNativeDriver: true,
}),
Animated.timing(mode, {
toValue: mode["_value"] === 0 ? 1 : 0,
duration: 150,
useNativeDriver: false,
}),
]).start();
};
const lockX = mode.interpolate({
inputRange: [0, 1],
outputRange: [windowWidth / 2 - 22, windowWidth / 2 - 22 - 60],
});
const lockY = mode.interpolate({
inputRange: [0, 1],
outputRange: [-20, -75],
});
const noteX = mode.interpolate({
inputRange: [0, 1],
outputRange: [windowWidth / 2 - 22, windowWidth / 2 - 22 + 60],
});
return (
<Box {...StyleSheet.absoluteFillObject} alignItems="center">
<Animated.View
style={[styles.secondaryView,{ left: lockX,top: lockY,}]}>
<TouchableOpacity style={styles.secondaryButton}>
<Feather name="lock" size={24} color="#FFF" />
</TouchableOpacity>
</Animated.View>
<Animated.View style={[styles.secondaryView,{ left: lockX,top: noteY,}]}>
<TouchableOpacity style={styles.secondaryButton}>
<Foundation name="clipboard-notes" size={24} color="#FFF" />
</TouchableOpacity>
</Animated.View>
<View style={[styles.button]}>
<Animated.View style={[transform: [{ scale: buttonSize }]]}>
<TouchableOpacity activeOpacity={1} onPress={handlePress} style={styles.primaryButton}
>
<Animated.View>
<FontAwesome5 name="plus" size={24} color="#FFF" />
</Animated.View>
</TouchableOpacity>
</Animated.View>
</View>
</Box>
);
};
I'd suggest something like this
childButtons = [
{
isPrimary: false,
iconName: 'lock',
iconType: 'Feather',
onPress: () => {},
},
{
isPrimary: false,
iconName: 'clipboard-notes',
iconType: 'Foundation',
onPress: () => {},
},
/** ... */
];
const AppTabSecondaryButton = ({ containerStyle, onPress, IconType, iconName }) => (
<Animated.View style={[styles.secondaryView, containerStyle]}>
<TouchableOpacity onPress={onPress} style={styles.secondaryButton}>
<IconType name={iconName} size={24} color="#FFF" />
</TouchableOpacity>
</Animated.View>
);
const AppTabPrimaryButton = ({ containerStyle, onPress, IconType, iconName }) => {
<View style={[styles.button]}>
<Animated.View style={containerStyle}>
<TouchableOpacity activeOpacity={1} onPress={onPress} style={styles.primaryButton}
>
<Animated.View>
<IconType name={iconName} size={24} color="#FFF" />
</Animated.View>
</TouchableOpacity>
</Animated.View>
</View>
};
export const TabAddButton = ({ childButtons }) => {
/** Other stuff */
const lockX = mode.interpolate({
inputRange: [0, 1],
outputRange: [windowWidth / 2 - 22, windowWidth / 2 - 22 - 60],
});
const lockY = mode.interpolate({
inputRange: [0, 1],
outputRange: [-20, -75],
});
const noteX = mode.interpolate({
inputRange: [0, 1],
outputRange: [windowWidth / 2 - 22, windowWidth / 2 - 22 + 60],
});
const btnContainerStyles = [
[styles.secondaryView, { left: lockX, top: lockY, }],
[styles.secondaryView, { left: lockX, top: noteY }],
/** ... */
];
return (
<Box {...StyleSheet.absoluteFillObject} alignItems="center">
{childButtons.map(({ iconName, iconType, onPress, isPrimary }, index) => (
isPrimary ? (<AppTabPrimaryButton /** supply props */ />) :
(<AppTabSecondaryButton /** supply props */ />)
))}
</Box>
);
};
I have the following code to create a 'dynamic' textinput. A label is placed above the textinput. When the textinput is focused, the label moves above the textinput and the fontsize and color changes. On blur of the textinput, the label moves back (if there is no input in the textinput).
This code contains react-native-reanimated code, should animations should work on the UI thread, not the JS thread.
When I test this code on an IOS simulator (tested iPhone 7 -> iPhone 11 Pro) and I focus on a textinput so the animation runs, the JS thread drops about 3-6 frames (54-57 fps), which is OK I guess. On a real device (iPhone 7), the JS thread drops about 20-30 frames (and sometimes even more). When I start typing in the textinput, I get a slow callback (I check the input of the textinput while typing). On the simulator, the callback is immediately. On the real device, it sometimes takes up to 2 seconds before the input is checked.
import React, { useState, useEffect } from 'react';
import { View, TextInput } from 'react-native';
import Animated, { Easing, Extrapolate } from 'react-native-reanimated';
import { interpolateColor, useTimingTransition } from 'react-native-redash';
import Colors from '../../constants/Colors';
const { interpolate } = Animated;
const AuthenticationInput = React.forwardRef((props, ref) => {
const {
ref,
label,
onChangeText,
secureTextEntry,
returnKeyType,
icon
} = props;
const [value, setValue] = useState('');
const [trans, setTrans] = useState(0);
const transition = useTimingTransition(trans, {
duration: 250,
easing: Easing.inOut(Easing.ease)
});
// move the label in the x direction
const moveX = interpolate(transition, {
inputRange: [0, 1],
outputRange: [12.5, 0],
extrapolate: Extrapolate.CLAMP
});
// move the label in the y direction
const moveY = interpolate(transition, {
inputRange: [0, 1],
outputRange: [12.5, -20],
extrapolate: Extrapolate.CLAMP
});
// change the font size of the label
const fontSize = interpolate(transition, {
inputRange: [0, 1],
outputRange: [15, 12.5],
extrapolate: Extrapolate.CLAMP
});
// change the color of the label
const color = interpolateColor(transition, {
inputRange: [0, 1],
outputRange: ['#aaa', '#000']
});
// pass the input of the textinput to the
// onChangeText function passed as a prop
useEffect(() => {
onChangeText(value)
}, [value]);
return (
<View style={{
marginHorizontal: 25,
marginTop: 25,
borderRadius: 5,
backgroundColor: Colors.white
}}
>
<Animated.View style={{
position: 'absolute',
transform: [{ translateX: moveX }, { translateY: moveY }]
}}
>
<Animated.Text style={{
fontSize,
color
}}
>
{label}
</Animated.Text>
</Animated.View>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TextInput
ref={ref}
autoCapitalize="none"
autoCompleteType="off"
autoCorrect={false}
secureTextEntry={secureTextEntry}
onChangeText={(v) => setValue(v)}
style={{
margin: 0,
padding: 12.5,
flex: 1,
fontSize: 15
}}
hitSlop={{
top: 10,
right: 10,
bottom: 10,
left: 10
}}
onFocus={() => setTrans(1)}
onBlur={() => {
if (value.length === 0) {
setTrans(0);
}
}}
returnKeyType={returnKeyType}
/>
<View style={{ padding: 12.5 }}>
{icon}
</View>
</View>
</View>
);
});
export default AuthenticationInput;