├── .expo-shared
└── assets.json
├── .gitignore
├── App.js
├── README.md
├── app.json
├── assets
├── favicon.png
├── icon.png
└── splash.png
├── babel.config.js
├── package.json
├── react-native-custom-drawer-animation-dribbble-preview.gif
├── utils.js
├── yarn-error.log
└── yarn.lock
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p8
6 | *.p12
7 | *.key
8 | *.mobileprovision
9 | *.orig.*
10 | web-build/
11 |
12 | # macOS
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | /*
2 | Inspiration: https://dribbble.com/shots/4275765-Realweb-3
3 | Twitter: http://twitter.com/mironcatalin
4 | YouTube: http://youtube.com/c/CatalinMironDev
5 | ☕️ BuyMeACoffee: https://www.buymeacoffee.com/catalinmiron
6 | */
7 |
8 | import * as React from 'react';
9 | import {
10 | StatusBar,
11 | Animated,
12 | Dimensions,
13 | Text,
14 | View,
15 | StyleSheet,
16 | TouchableOpacity,
17 | } from 'react-native';
18 | import Svg, { Polygon } from 'react-native-svg';
19 | import { AntDesign } from '@expo/vector-icons';
20 | import MaskedView from '@react-native-community/masked-view';
21 | import { colors, links, routes } from './utils';
22 |
23 | const AnimatedAntDesign = Animated.createAnimatedComponent(AntDesign);
24 | const AnimatedPolygon = Animated.createAnimatedComponent(Polygon);
25 |
26 | const { width, height } = Dimensions.get('window');
27 | const fromCoords = { x: 0, y: height };
28 | const toCoords = { x: width, y: 0 };
29 |
30 | const Button = ({ title, onPress, style }) => {
31 | return (
32 |
33 | {title}
34 |
35 | );
36 | };
37 |
38 | const Drawer = ({ animation, onPress }) => {
39 | const polygonRef = React.useRef();
40 |
41 | React.useEffect(() => {
42 | const listener = animation.addListener((v) => {
43 | if (polygonRef?.current) {
44 | polygonRef.current.setNativeProps({
45 | points: `0,0 ${v.x}, ${v.y} ${width}, ${height} 0, ${height}`,
46 | });
47 | }
48 | });
49 |
50 | return () => {
51 | animation.removeListener(listener);
52 | };
53 | });
54 |
55 | const opacity = animation.x.interpolate({
56 | inputRange: [0, width],
57 | outputRange: [0, 1],
58 | });
59 |
60 | const translateX = animation.x.interpolate({
61 | inputRange: [0, width],
62 | outputRange: [-50, 0],
63 | });
64 |
65 | const [selectedRoute, setSelectedRoute] = React.useState('Get started');
66 | const onRoutePress = React.useCallback((route) => {
67 | setSelectedRoute(route);
68 | onPress();
69 | }, []);
70 |
71 | return (
72 |
81 |
87 |
88 | }
89 | >
90 |
91 |
98 |
101 |
102 | {routes.map((route, index) => {
103 | return (
104 |
120 |
121 |
122 | {links.map((link, index) => {
123 | return (
124 |
133 | );
134 | })}
135 |
136 |
137 |
138 |
139 | );
140 | };
141 |
142 | export default function App() {
143 | const animation = React.useRef(new Animated.ValueXY(fromCoords)).current;
144 | const animate = (toValue) => {
145 | return Animated.spring(animation, {
146 | toValue: toValue === 1 ? toCoords : fromCoords,
147 | useNativeDriver: true,
148 | bounciness: 2,
149 | speed: 10,
150 | });
151 | };
152 | const opacity = animation.x.interpolate({
153 | inputRange: [0, width],
154 | outputRange: [1, 0],
155 | });
156 |
157 | const onCloseDrawer = React.useCallback(() => {
158 | StatusBar.setBarStyle('dark-content', true);
159 | animate(0).start();
160 | }, []);
161 | const onOpenDrawer = React.useCallback(() => {
162 | StatusBar.setBarStyle('light-content', true);
163 | animate(1).start();
164 | }, []);
165 |
166 | return (
167 |
168 |
169 |
170 |
177 |
178 | );
179 | }
180 |
181 | const styles = StyleSheet.create({
182 | maskedContainer: {
183 | flex: 1,
184 | },
185 | menuContainer: {
186 | flex: 1,
187 | backgroundColor: '#222',
188 | paddingTop: 80,
189 | paddingHorizontal: 20,
190 | paddingBottom: 20,
191 | },
192 | menu: {
193 | flex: 1,
194 | alignItems: 'flex-start',
195 | justifyContent: 'space-between',
196 | },
197 | button: {
198 | fontSize: 32,
199 | color: '#fdfdfd',
200 | lineHeight: 32 * 1.5,
201 | },
202 | buttonSmall: {
203 | fontSize: 16,
204 | marginBottom: 5,
205 | color: '#fdfdfd',
206 | },
207 | });
208 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Native Custom Drawer Animation
2 |
3 |
4 |
5 | ## Run on your device
6 |
7 | Snack: https://snack.expo.io/@catalinmiron/react-native-svg-drawer-animation
8 |
9 | ### Youtube tutorial
10 |
11 | [](https://youtu.be/fgUHYGeoG5U)
12 |
13 | In this tutorial we will build an custom drawer in React Native using SVG, Masked View and vanilla Animated API from React Native.
14 | We will go through how to animate a Polygon in React Native, how to animate the inner content of the drawer and how the React Native MaskedView is working.
15 |
16 | - Inspiration: https://dribbble.com/shots/4275765-Realweb-3
17 | - SourceCode: http://github.com/catalinmiron/react-native-custom-drawer-animation
18 |
19 | ---
20 |
21 | - React Native Masked view: https://github.com/react-native-community/react-native-masked-view
22 | - React Native SVG: https://github.com/react-native-community/react-native-svg
23 | - React Native vector icons: https://github.com/oblador/react-native-vector-icons
24 | - Expo: https://expo.io/
25 |
26 | Want to support me?
27 |
28 | - BuyMeACoffee: https://www.buymeacoffee.com/catalinmiron
29 | - Paypal: https://paypal.me/catalinmiron
30 |
31 | You can find me on:
32 |
33 | - Github: http://github.com/catalinmiron
34 | - Twitter: http://twitter.com/mironcatalin
35 | - Website: http://batman.codes
36 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "react-native-drawer-animation",
4 | "slug": "react-native-drawer-animation",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "splash": {
9 | "image": "./assets/splash.png",
10 | "resizeMode": "contain",
11 | "backgroundColor": "#ffffff"
12 | },
13 | "updates": {
14 | "fallbackToCacheTimeout": 0
15 | },
16 | "assetBundlePatterns": [
17 | "**/*"
18 | ],
19 | "ios": {
20 | "supportsTablet": true
21 | },
22 | "web": {
23 | "favicon": "./assets/favicon.png"
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catalinmiron/react-native-custom-drawer-animation/0537b914f92e34eaf203b198659576aba5a974f3/assets/favicon.png
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catalinmiron/react-native-custom-drawer-animation/0537b914f92e34eaf203b198659576aba5a974f3/assets/icon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catalinmiron/react-native-custom-drawer-animation/0537b914f92e34eaf203b198659576aba5a974f3/assets/splash.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "web": "expo start --web",
8 | "eject": "expo eject"
9 | },
10 | "dependencies": {
11 | "@expo/vector-icons": "^10.0.0",
12 | "@react-native-community/masked-view": "0.1.10",
13 | "@react-navigation/native": "react-navigation/native",
14 | "expo": "~38.0.8",
15 | "expo-status-bar": "^1.0.2",
16 | "react": "~16.11.0",
17 | "react-dom": "~16.11.0",
18 | "react-native": "https://github.com/expo/react-native/archive/sdk-38.0.2.tar.gz",
19 | "react-native-svg": "12.1.0",
20 | "react-native-web": "~0.11.7",
21 | "react-navigation-drawer": "react-navigation/drawer"
22 | },
23 | "devDependencies": {
24 | "@babel/core": "^7.8.6",
25 | "babel-preset-expo": "~8.1.0"
26 | },
27 | "private": true
28 | }
29 |
--------------------------------------------------------------------------------
/react-native-custom-drawer-animation-dribbble-preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/catalinmiron/react-native-custom-drawer-animation/0537b914f92e34eaf203b198659576aba5a974f3/react-native-custom-drawer-animation-dribbble-preview.gif
--------------------------------------------------------------------------------
/utils.js:
--------------------------------------------------------------------------------
1 | export const routes = [
2 | 'Get started',
3 | 'Features',
4 | 'Tools',
5 | 'Services',
6 | 'Portfolio',
7 | 'Careers',
8 | 'Contact',
9 | ];
10 | export const links = ['Follow us', 'Quota', 'Awesome link'];
11 | export const colors = [
12 | '#69d2e7',
13 | '#a7dbd8',
14 | '#e0e4cc',
15 | '#f38630',
16 | '#fa6900',
17 | '#fe4365',
18 | '#fc9d9a',
19 | '#f9cdad',
20 | '#c8c8a9',
21 | '#83af9b',
22 | '#ecd078',
23 | '#d95b43',
24 | '#c02942',
25 | '#53777a',
26 | ];
27 |
--------------------------------------------------------------------------------