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>
);
};
Related
const heightAnim = useRef(new Animated.Value(0)).current
const heightInterpolate = heightAnim.interpolate({
inputRange: [0,1],
outputRange: ['auto',500],
})
The above code not working because error invalid pattern. 'auto' is
String and 500 is number.
I try to set
const value2 = 500,
outputRange: ['auto',value2],
but as I expected it didn't work
How can set Value of animated to 'auto'?
Here is an example using LayoutAnimation
if (Platform.OS === "android") {
if (UIManager.setLayoutAnimationEnabledExperimental) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
.....
const App = () => {
.....
<View style={styles.header}>
<TouchableOpacity
onPress={() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
setExpanded(!expanded);
}}
>
<Text>Press me to {expanded ? "collapse" : "expand"}!</Text>
</TouchableOpacity>
<View
style={{ backgroundColor: "red", height: expanded ? "auto" : 500 }}
>
<Text>Test</Text>
<Text>Test</Text>
<Text>Test</Text>
<Text>Test</Text>
<Text>Test</Text>
</View>
</View>
....
We can call ConfigureNext with 3 presets
LayoutAnimation.Presets.easeInEaseOut
LayoutAnimation.Presets.linear
LayoutAnimation.Presets.spring
You can even create your custom confige
ConfigureNext({ duration: 700, update: { type: 'spring', springDamping: 0.4 },})
Check Documentation for more information
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 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!!
Im using: react-native, expo,
react-native-gesture-handler/Swipeable
here is a code of screen https://github.com/ArturScwaiberov/react-native-dental-app/blob/master/screens/HomeScreen.js
In my app there is list of appointments rendered by SectionList.
In renderItem I created two buttons, one of them is to delete item.
So, I cant understand how should I pass appointment ID to renderRightActions to delete or edit this current appointment.. please help me find out the solution!
Here is my HomeScreen code review:
const HomeScreen = ({ navigation }) => {
const [data, setData] = useState(null)
const [refreshing, setRefreshing] = useState(false)
const fetchAppointments = () => {
setRefreshing(true)
appointmentsApi
.get()
.then(({ data }) => {
setData(data.message)
setRefreshing(false)
})
.catch((e) => {
setRefreshing(false)
console.log(e)
})
}
useEffect(fetchAppointments, [])
const removeAppointment = (id) => {
console.log(id)
const result = data.map((group) => {
group.data = group.data.filter((item) => item._id !== id)
return group
})
setData(result)
//appointmentsApi.remove(id)
}
renderRightAction = (text, color, x, progress) => {
const trans = progress.interpolate({
inputRange: [0, 1],
outputRange: [x, 0],
})
const pressHandler = () => {
if (text === 'pencil') {
alert('hey')
} else {
//but how to get over here the ID of item from SectionList?
removeAppointment(id)
}
}
return (
<Animated.View style={{ flex: 1, transform: [{ translateX: trans }] }}>
<RectButton
style={{
alignItems: 'center',
flex: 1,
justifyContent: 'center',
backgroundColor: color,
}}
onPress={pressHandler}
>
<ActionText>
<Octicons name={text} size={24} color='white' />
</ActionText>
</RectButton>
</Animated.View>
)
}
renderRightActions = (progress) => (
<RightButtonsHandler>
{renderRightAction('pencil', '#B4C1CB', 160, progress)}
{renderRightAction('trashcan', '#F85A5A', 80, progress)}
</RightButtonsHandler>
)
return (
<Container>
<SectionList
style={{ paddingLeft: 20, paddingRight: 20 }}
sections={data}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => (
<Swipeable renderRightActions={renderRightActions} friction={2}>
<Appointment navigation={navigation} item={item} />
</Swipeable>
)}
renderSectionHeader={({ section: { title } }) => <SectionTitle>{title}</SectionTitle>}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={fetchAppointments} />}
/>
<PluseButton
style={{
shadowColor: '#2A86FF',
shadowOffset: {
width: 0,
height: 4,
},
shadowOpacity: 0.3,
shadowRadius: 4.65,
elevation: 8,
}}
onPress={() => navigation.navigate('AddPatient')}
>
<Ionicons name='ios-add' size={32} color='white' />
</PluseButton>
</Container>
)
}
You only need to pass the item id as a function param.
renderRightActions={(progress) => renderRightActions(progress, item.id)}
I made all changes. Try this code:
const HomeScreen = ({ navigation }) => {
const [data, setData] = useState(null)
const [refreshing, setRefreshing] = useState(false)
const fetchAppointments = () => {
setRefreshing(true)
appointmentsApi
.get()
.then(({ data }) => {
setData(data.message)
setRefreshing(false)
})
.catch((e) => {
setRefreshing(false)
console.log(e)
})
}
useEffect(fetchAppointments, [])
const removeAppointment = (id) => {
console.log(id)
const result = data.map((group) => {
group.data = group.data.filter((item) => item._id !== id)
return group
})
setData(result)
//appointmentsApi.remove(id)
}
renderRightAction = (text, color, x, progress, id) => {
const trans = progress.interpolate({
inputRange: [0, 1],
outputRange: [x, 0],
})
const pressHandler = () => {
if (text === 'pencil') {
alert('hey')
} else {
// but how to get over here the ID of item from SectionList?
removeAppointment(id) // its simple! :)
}
}
return (
<Animated.View style={{ flex: 1, transform: [{ translateX: trans }] }}>
<RectButton
style={{
alignItems: 'center',
flex: 1,
justifyContent: 'center',
backgroundColor: color,
}}
onPress={pressHandler}
>
<ActionText>
<Octicons name={text} size={24} color='white' />
</ActionText>
</RectButton>
</Animated.View>
)
}
renderRightActions = (progress, id) => (
<RightButtonsHandler>
{renderRightAction('pencil', '#B4C1CB', 160, progress, id)}
{renderRightAction('trashcan', '#F85A5A', 80, progress, id)}
</RightButtonsHandler>
)
return (
<Container>
<SectionList
style={{ paddingLeft: 20, paddingRight: 20 }}
sections={data}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => (
<Swipeable renderRightActions={(progress) => renderRightActions(progress, item.id)} friction={2}>
<Appointment navigation={navigation} item={item} />
</Swipeable>
)}
renderSectionHeader={({ section: { title } }) => <SectionTitle>{title}</SectionTitle>}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={fetchAppointments} />}
/>
<PluseButton
style={{
shadowColor: '#2A86FF',
shadowOffset: {
width: 0,
height: 4,
},
shadowOpacity: 0.3,
shadowRadius: 4.65,
elevation: 8,
}}
onPress={() => navigation.navigate('AddPatient')}
>
<Ionicons name='ios-add' size={32} color='white' />
</PluseButton>
</Container>
)
}
I am trying to make the screen flip like a coin when button pressed on it. What I have done up-till now is I have successfully flipped two small screens, but it comes to a bit large screen the code is not working. Like here in my code two simple buttons are working fine, but when I call view from other big screen, the view doesn't show up. I think it is because the view that will is flipped becomes invisible but remains on its place and other view can't be placed on that space, so when it comes to larger screen the whole thing doesn't show up. I am new to RN. Help.
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableOpacity,
Animated
} from 'react-native';
var screenWidth = require('Dimensions').get('window').width;
export default class animatedbasic extends Component {
componentWillMount() {
this.animatedValue = new Animated.Value(0);
this.value = 0;
this.animatedValue.addListener(({ value }) => {
this.value = value;
})
this.frontInterpolate = this.animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg'],
})
this.backInterpolate = this.animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['180deg', '360deg']
})
this.frontOpacity = this.animatedValue.interpolate({
inputRange: [89, 90],
outputRange: [1, 0]
})
this.backOpacity = this.animatedValue.interpolate({
inputRange: [89, 90],
outputRange: [0, 1]
})
}
flipCard() {
if (this.value >= 90) {
Animated.spring(this.animatedValue,{
toValue: 0,
friction: 8,
tension: 10
}).start();
} else {
Animated.spring(this.animatedValue,{
toValue: 180,
friction: 8,
tension: 10
}).start();
}
}
render() {
const frontAnimatedStyle = {
transform: [
{ rotateY: this.frontInterpolate }
]
}
const backAnimatedStyle = {
transform: [
{ rotateY: this.backInterpolate }
]
}
return (
<View style={{ flex:1, justifyContent:'center', alignItems:'center'}} >
<View >
<Animated.View style={[styles.flipCard, frontAnimatedStyle, {opacity: this.frontOpacity}]}>
</Animated.View>
<Animated.View style={[styles.flipCard, styles.flipCardBack, backAnimatedStyle, {opacity: this.backOpacity}]}>
<View>
<TouchableOpacity onPress={() => this.flipCard()} style={{ width:(screenWidth/2), height:70, backgroundColor:'black'}}>
<Text style={{color:'white', fontSize:22, fontWeight:'bold'}}> I am on Back</Text>
</TouchableOpacity>
</View>
</Animated.View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
flipCard: {
backfaceVisibility: 'hidden',
},
flipCardBack: {
top: 0,
},
});
I have also tried react-native-card-flip
Flipping.js
render() {
return (
<CardFlip style={styles.cardContainer} ref={(card) => this.card
=card}>
<FronEnd />
<Backend />
</CardFlip> }
FronEnd.js
render()
{
return (
<View>
<CardFlip style={styles.cardContainer} ref={(card) => this.card = card} >
....................................................
<TouchableOpacity onPress={() => this.card.flip()}
</TouchableOpacity >
</CardFlip>
</View>
);
}
}
Backend.js
render()
{
return (
<View>
<CardFlip style={styles.cardContainer} ref={(card) => this.card = card} >
.......................
<TouchableOpacity onPress={() => this.card.flip()}
</TouchableOpacity >
</CardFlip>
</View>
);
}
You can use a npm module for this :
Installation :
npm install --save react-native-card-flip
Usage :
import CardFlip from 'react-native-card-flip';
<CardFlip style={styles.cardContainer} ref={(card) => this.card = card} >
<TouchableOpacity style={styles.card} onPress={() => this.card.flip()} ><Text>AB</Text></TouchableOpacity>
<TouchableOpacity style={styles.card} onPress={() => this.card.flip()} ><Text>CD</Text></TouchableOpacity>
</CardFlip>
See Output
Npm module : Github