├── .gitattributes ├── App.js ├── README.md ├── app.json ├── assets └── icons │ ├── app-icon.png │ └── loading-icon.png ├── components └── Button.js ├── package.json └── utils ├── index.js ├── normalizeSizes.js └── withPressAnimation.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Text } from 'react-native'; 4 | import styled from 'styled-components'; 5 | import Button from './components/Button'; 6 | import { normalize } from './utils'; 7 | 8 | const Wrapper = styled.View` 9 | align-self: center; 10 | justify-content: center; 11 | background-color: #000; 12 | flex: 1; 13 | width: 100%; 14 | `; 15 | 16 | const App = props => ( 17 | 18 | 19 | 20 | ); 21 | 22 | export default App; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Example usage of withPressAnimation higher order component on Button component 2 | 3 | # React Native `withPressAnimation` 4 | 5 | A higher order component which provides onPress animation for React Native component. 6 | Demo 👉[Expo Snack](https://snack.expo.io/@danijelgrabez/withpressanimation). 7 | 8 | ### Props which will be received from the component 9 | - `onPress` 10 | - `disabled` 11 | 12 | 13 | ### Usage 14 | 15 | ``` 16 | import withPressAnimation from './path/to/file'; 17 | 18 | const Button = props => ( 19 | ... 20 | ) 21 | 22 | export default withPressAnimation(Button); 23 | ``` 24 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "withpressanimation", 4 | "description": "No description", 5 | "slug": "snack-HymSDsUSE", 6 | "privacy": "unlisted", 7 | "sdkVersion": "32.0.0", 8 | "version": "1.0.0", 9 | "orientation": "portrait", 10 | "primaryColor": "#cccccc", 11 | "icon": "https://d1wp6m56sqw74a.cloudfront.net/~assets/c9aa1be8a6a6fe81e20c3ac4106a2ebc", 12 | "loading": { 13 | "icon": "https://d1wp6m56sqw74a.cloudfront.net/~assets/c9aa1be8a6a6fe81e20c3ac4106a2ebc", 14 | "hideExponentText": false 15 | }, 16 | "packagerOpts": { 17 | "assetExts": [ 18 | "ttf", 19 | "mp4", 20 | "otf", 21 | "xml" 22 | ] 23 | }, 24 | "ios": { 25 | "supportsTablet": true 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /assets/icons/app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danijelgrabez/react-native-withPressAnimation/081258b8ab87060b34354f8f61700eacaef76780/assets/icons/app-icon.png -------------------------------------------------------------------------------- /assets/icons/loading-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danijelgrabez/react-native-withPressAnimation/081258b8ab87060b34354f8f61700eacaef76780/assets/icons/loading-icon.png -------------------------------------------------------------------------------- /components/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styled from 'styled-components'; 4 | import normalize from '../utils/normalizeSizes'; 5 | import withPressAnimation from '../utils/withPressAnimation'; 6 | 7 | export const ButtonStyles = styled.View` 8 | align-self: center; 9 | justify-content: center; 10 | background-color: #ffd200; 11 | padding-horizontal: ${normalize(30)}; 12 | height: ${normalize(60)}; 13 | border-radius: ${normalize(30)}; 14 | align-items: center; 15 | shadow-color: #ffd200; 16 | shadow-opacity: 0.8; 17 | shadow-radius: 30; 18 | `; 19 | 20 | export const TextStyles = styled.Text` 21 | color: #000; 22 | font-size: ${normalize(25)}; 23 | `; 24 | 25 | /** 26 | * Button component 27 | */ 28 | const Button = ({ 29 | children, 30 | loading, 31 | ...props 32 | }) => ( 33 | 34 | {loading || children} 35 | 36 | ); 37 | 38 | Button.propTypes = { 39 | /** 40 | * Textual content which will be passed to component 41 | */ 42 | children: PropTypes.string, 43 | /** 44 | * Text for loading state (e.g. during form submission) 45 | */ 46 | loading: PropTypes.string, 47 | }; 48 | 49 | Button.defaultProps = { 50 | children: null, 51 | loading: null, 52 | }; 53 | 54 | export default withPressAnimation(Button); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-withPressAnimation", 3 | "version": "0.0.0", 4 | "description": "Press animation higher order component for React Native.", 5 | "author": "Danijel Grabez", 6 | "private": true, 7 | "main": "node_modules/expo/AppEntry.js", 8 | "dependencies": { 9 | "expo": "^32.0.0", 10 | "react": "16.5.0", 11 | "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz", 12 | "memoize-one": "5.0.0", 13 | "prop-types": "15.7.2", 14 | "styled-components": "4.1.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /utils/index.js: -------------------------------------------------------------------------------- 1 | export { normalize } from './normalizeSizes'; 2 | export { default as withPressAnimation } from './withPressAnimation'; -------------------------------------------------------------------------------- /utils/normalizeSizes.js: -------------------------------------------------------------------------------- 1 | import memoizeOne from 'memoize-one'; 2 | import { Dimensions, Platform, PixelRatio } from 'react-native'; 3 | 4 | const normalize = memoizeOne((size) => { 5 | const { width: SCREEN_WIDTH } = Dimensions.get('window'); 6 | 7 | // based on iphone 5s's scale 8 | const scale = SCREEN_WIDTH / 320; 9 | 10 | const newSize = size * scale; 11 | if (Platform.OS === 'ios') { 12 | return Math.round(PixelRatio.roundToNearestPixel(newSize)); 13 | } 14 | 15 | return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 2; 16 | }); 17 | 18 | export default normalize; -------------------------------------------------------------------------------- /utils/withPressAnimation.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Animated, TouchableWithoutFeedback } from 'react-native'; 3 | import PropTypes from 'prop-types'; 4 | 5 | const withPress = Component => class WithPress extends React.Component { 6 | static propTypes = { 7 | /** 8 | * Pass onPress handler 9 | */ 10 | onPress: PropTypes.func, 11 | /** 12 | * Pass disabled handler 13 | */ 14 | disabled: PropTypes.bool, 15 | }; 16 | 17 | static defaultProps = { 18 | onPress: () => {}, 19 | disabled: false, 20 | }; 21 | 22 | state = { 23 | animatedStartValue: new Animated.Value(1), 24 | } 25 | 26 | handlePressIn = () => { 27 | const { animatedStartValue } = this.state; 28 | Animated.timing( 29 | animatedStartValue, 30 | { 31 | toValue: 0.8, 32 | duration: 200, 33 | useNativeDriver: true, 34 | }, 35 | ).start(); 36 | } 37 | 38 | handlePressOut = () => { 39 | const { animatedStartValue } = this.state; 40 | Animated.timing( 41 | animatedStartValue, 42 | { 43 | toValue: 1, 44 | duration: 200, 45 | useNativeDriver: true, 46 | }, 47 | ).start(); 48 | } 49 | 50 | render() { 51 | const { onPress, disabled } = this.props; 52 | const { animatedStartValue } = this.state; 53 | const animatedStyle = { 54 | transform: [{ scale: animatedStartValue }], 55 | }; 56 | 57 | return ( 58 | 59 | 65 | 66 | 67 | 68 | ); 69 | } 70 | }; 71 | 72 | export default withPress; 73 | --------------------------------------------------------------------------------