AnimatedButton
Button with smooth press feedback using timing-based animations.
9:41
signal_cellular_altwifibattery_full
Preview Controls
Animation
Press and release
This is a CSS-based preview of the animation. The actual React Native component uses physics-based springs for a more natural feel.
Installation
$
Usage
Example.tsx
import { AnimatedButton } from './components/AnimatedButton';export default function App() {return (<AnimatedButton onPress={() => console.log('Pressed!')}>Press Me</AnimatedButton>);}
Full Component Code
AnimatedButton.tsx
1import React from 'react';2import {3 TouchableWithoutFeedback,4 ViewStyle,5 StyleProp,6 TextStyle,7 StyleSheet,8 Text,9} from 'react-native';10import Animated, {11 useSharedValue,12 useAnimatedStyle,13 withTiming,14 interpolateColor,15 Easing,16} from 'react-native-reanimated';1718interface AnimatedButtonProps {19 onPress?: () => void;20 children: React.ReactNode;21 style?: StyleProp<ViewStyle>;22 textStyle?: StyleProp<TextStyle>;23 pressScale?: number;24 duration?: number;25 backgroundColor?: string;26 pressedBackgroundColor?: string;27 textColor?: string;28 disabled?: boolean;29}3031export const AnimatedButton: React.FC<AnimatedButtonProps> = ({32 onPress,33 children,34 style,35 textStyle,36 pressScale = 0.95,37 duration = 150,38 backgroundColor = '#c43b15',39 pressedBackgroundColor = '#c4320a',40 textColor = '#ffffff',41 disabled = false,42}) => {43 const scale = useSharedValue(1);44 const pressed = useSharedValue(0);4546 const timingConfig = {47 duration,48 easing: Easing.out(Easing.ease),49 };5051 const handlePressIn = () => {52 scale.value = withTiming(pressScale, timingConfig);53 pressed.value = withTiming(1, timingConfig);54 };5556 const handlePressOut = () => {57 scale.value = withTiming(1, timingConfig);58 pressed.value = withTiming(0, timingConfig);59 };6061 const animatedStyle = useAnimatedStyle(() => ({62 transform: [{ scale: scale.value }],63 backgroundColor: interpolateColor(64 pressed.value,65 [0, 1],66 [backgroundColor, pressedBackgroundColor]67 ),68 }));6970 const renderChildren = () => {71 if (typeof children === 'string' || typeof children === 'number') {72 return (73 <Text style={[styles.text, { color: textColor }, textStyle]}>74 {children}75 </Text>76 );77 }78 return children;79 };8081 return (82 <TouchableWithoutFeedback83 onPress={onPress}84 onPressIn={handlePressIn}85 onPressOut={handlePressOut}86 disabled={disabled}87 >88 <Animated.View style={[styles.button, style, animatedStyle, disabled && styles.disabled]}>89 {renderChildren()}90 </Animated.View>91 </TouchableWithoutFeedback>92 );93};9495const styles = StyleSheet.create({96 button: {97 paddingVertical: 14,98 paddingHorizontal: 28,99 borderRadius: 12,100 alignItems: 'center',101 justifyContent: 'center',102 },103 text: {104 fontSize: 16,105 fontWeight: '600',106 },107 disabled: {108 opacity: 0.5,109 },110});111112export default AnimatedButton;
Props
| Name | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | - | Button content (auto-wraps strings in Text) |
| onPress | () => void | - | Press handler |
| pressScale | number | 0.95 | Scale when pressed |
| duration | number | 150 | Animation duration in ms |
| backgroundColor | string | '#c43b15' | Default background color |
| pressedBackgroundColor | string | '#c4320a' | Background when pressed |
| textColor | string | '#ffffff' | Text color for string children |
| disabled | boolean | false | Disable the button |
