Reputation: 2000
I'm creating a touchable button in react native with an animation. When the button is pressed, it should scale down a little bit. When the pressure is released, it should scale back to normal.
This is my code:
export const TouchableButton = (props) => {
const { onPress, text, icon } = props
const animatedValue = new Animated.Value(0)
const animatedValueInterpolateScale = animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [1, 0.95]
})
const pressInHandler = () => {
Animated.timing(
animatedValue,
{
toValue: 1,
duration: 150
}
).start()
}
const pressOutHandler = () => {
Animated.timing(
animatedValue,
{
toValue: 0,
duration: 150
}
).start()
}
return (
<TouchableWithoutFeedback onPress={onPress} onPressIn={pressInHandler} onPressOut={pressOutHandler}>
<View style={{ alignItems: 'center' }}>
<Animated.View style={{ width: '100%', height: 40, borderRadius: 5, overflow: 'hidden', transform: [{ scaleX: animatedValueInterpolateScale }, { scaleY: animatedValueInterpolateScale }] }}>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: Color.GrayLight }}>
<Text style={{ marginTop: 2.5, fontFamily: 'AlegreyaSans-Medium', fontSize: 15, color: Color.White }}>{text}</Text>
<View style={{ position: 'absolute', left: 12.5, top: 12.5 }}>
<Icon lib={icon.lib} icon={icon.icon} color={Color.White} size={15} />
</View>
</View>
</Animated.View>
</View>
</TouchableWithoutFeedback>
)
}
When the button is pressed, the animation in pressInHandler is started, and the scale is animated from 1 to 0.95. This works. But when I release the pressure (onPressOut is called), the scale snaps back to 1 without a smooth animation. It seems like pressOutHandler (and the animation in it) never is called.
I have another button with the same properties but instead of scaling I set the background color, and this works like it should.
Upvotes: 4
Views: 11495
Reputation: 5524
Here is a pretty simple solution without any animations which looks almost as native (at least on iOS):
import React from "react"
import { Pressable, PressableProps, StyleProp, ViewStyle } from "react-native"
type TouchableButtonProps = PressableProps & {
scale?: number;
style?: StyleProp<ViewStyle>;
}
const PressableScale: React.FC<TouchableButtonProps> = ({ scale, style, children, ...otherProps }) => {
return (
<Pressable style={({ pressed }) => [style, { transform: [{ scale: pressed ? (scale ?? 0.98) : 1 }] }]} {...otherProps}>
{children}
</Pressable>
)
}
Usage:
<PressableScale style={{ flex: 1, justifyContent: 'center', alignContent: 'center', backgroundColor: 'black', padding: 50, borderRadius: 12 }}>
<Text style={{ color: 'white' }}>This is pressable button</Text>
</PressableScale>
Upvotes: 2
Reputation: 1222
Make it simple.
Note: ALWAYS USE useNativeDriver: true
const App = () => {
const animation = new Animated.Value(0);
const inputRange = [0, 1];
const outputRange = [1, 0.8];
const scale = animation.interpolate({inputRange, outputRange});
const onPressIn = () => {
Animated.spring(animation, {
toValue: 1,
useNativeDriver: true,
}).start();
};
const onPressOut = () => {
Animated.spring(animation, {
toValue: 0,
useNativeDriver: true,
}).start();
};
return (
<View style={styles.container}>
<Animated.View style={[styles.button, {transform: [{scale}]}]}>
<TouchableOpacity
style={styles.btn}
activeOpacity={1}
onPressIn={onPressIn}
onPressOut={onPressOut}>
<Text style={styles.btnText}>BUTTON</Text>
</TouchableOpacity>
</Animated.View>
</View>
);
};
export default App;
const styles = StyleSheet.create({
container: {flex: 1, alignItems: 'center', justifyContent: 'center'},
button: {
height: 70,
width: 200,
backgroundColor: 'red',
marginBottom: 20,
borderRadius: 10,
},
btn: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
btnText: {
color: '#fff',
fontSize: 25,
},
});
Upvotes: 7