├── .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 |
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 |
--------------------------------------------------------------------------------