├── .eslintrc.js ├── App.js ├── app.json ├── assets ├── fonts │ └── SpaceMono-Regular.ttf └── images │ ├── doggo.jpeg │ ├── icon.png │ ├── robot-dev.png │ ├── robot-prod.png │ └── splash.png ├── babel.config.js ├── components ├── StyledText.js └── TabBarIcon.js ├── constants ├── Colors.js └── Layout.js ├── navigation ├── AppNavigator.js └── MainTabNavigator.js ├── package.json ├── screens ├── ClientSays.js ├── LinksScreen.js ├── SettingsScreen.js ├── Task0.js ├── Task0b.js ├── Task1.js ├── Task2.js ├── Task3.js ├── Task4.js ├── Task5.js ├── Task6.js └── react-hexagon.png └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "parser": "babel-eslint", 7 | 'extends': [ 8 | 'plugin:react/recommended' 9 | ], 10 | "parserOptions": { 11 | "ecmaFeatures": { 12 | "jsx": true 13 | }, 14 | "ecmaVersion": 2018, 15 | "sourceType": "module" 16 | }, 17 | "plugins": [ 18 | "react" 19 | ], 20 | "rules": { 21 | "indent": [ 22 | "error", 23 | 2 24 | ], 25 | "max-len": [ 26 | 0, 27 | 80, 28 | ], 29 | "no-undef": [ 30 | 0 31 | ], 32 | "linebreak-style": [ 33 | "error", 34 | "unix" 35 | ], 36 | "quotes": [ 37 | "error", 38 | "single" 39 | ], 40 | "semi": [ 41 | "error", 42 | "never" 43 | ], 44 | "react/prop-types": [ 45 | 0 46 | ] 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Platform, StatusBar, StyleSheet, View } from 'react-native' 3 | import { AppLoading, Asset, Font, Icon } from 'expo' 4 | import AppNavigator from './navigation/AppNavigator' 5 | 6 | export default class App extends React.Component { 7 | state = { 8 | isLoadingComplete: false, 9 | }; 10 | 11 | render() { 12 | if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) { 13 | return ( 14 | 19 | ) 20 | } else { 21 | return ( 22 | 23 | {Platform.OS === 'ios' && } 24 | 25 | 26 | ) 27 | } 28 | } 29 | 30 | _loadResourcesAsync = async () => { 31 | return Promise.all([ 32 | Asset.loadAsync([ 33 | require('./assets/images/robot-dev.png'), 34 | require('./assets/images/robot-prod.png'), 35 | ]), 36 | Font.loadAsync({ 37 | // This is the font that we are using for our tab bar 38 | ...Icon.Ionicons.font, 39 | // We include SpaceMono because we use it in HomeScreen.js. Feel free 40 | // to remove this if you are not using it in your app 41 | 'space-mono': require('./assets/fonts/SpaceMono-Regular.ttf'), 42 | }), 43 | ]) 44 | }; 45 | 46 | _handleLoadingError = error => { 47 | // In this case, you might want to report the error to your error 48 | // reporting service, for example Sentry 49 | console.warn(error) 50 | }; 51 | 52 | _handleFinishLoading = () => { 53 | this.setState({ isLoadingComplete: true }) 54 | }; 55 | } 56 | 57 | const styles = StyleSheet.create({ 58 | container: { 59 | flex: 1, 60 | backgroundColor: '#fff', 61 | }, 62 | }) 63 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "workshop", 4 | "slug": "workshop", 5 | "privacy": "public", 6 | "sdkVersion": "32.0.0", 7 | "platforms": [ 8 | "ios", 9 | "android" 10 | ], 11 | "version": "1.0.0", 12 | "orientation": "portrait", 13 | "icon": "./assets/images/icon.png", 14 | "splash": { 15 | "image": "./assets/images/splash.png", 16 | "resizeMode": "contain", 17 | "backgroundColor": "#ffffff" 18 | }, 19 | "updates": { 20 | "fallbackToCacheTimeout": 0 21 | }, 22 | "assetBundlePatterns": [ 23 | "**/*" 24 | ], 25 | "ios": { 26 | "supportsTablet": true 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osdnk/appjs-workshop/478cb3dadb2504b9a6b0bfe25b90a1ae75b2d0f0/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /assets/images/doggo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osdnk/appjs-workshop/478cb3dadb2504b9a6b0bfe25b90a1ae75b2d0f0/assets/images/doggo.jpeg -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osdnk/appjs-workshop/478cb3dadb2504b9a6b0bfe25b90a1ae75b2d0f0/assets/images/icon.png -------------------------------------------------------------------------------- /assets/images/robot-dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osdnk/appjs-workshop/478cb3dadb2504b9a6b0bfe25b90a1ae75b2d0f0/assets/images/robot-dev.png -------------------------------------------------------------------------------- /assets/images/robot-prod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osdnk/appjs-workshop/478cb3dadb2504b9a6b0bfe25b90a1ae75b2d0f0/assets/images/robot-prod.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osdnk/appjs-workshop/478cb3dadb2504b9a6b0bfe25b90a1ae75b2d0f0/assets/images/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true) 3 | return { 4 | presets: ['babel-preset-expo'], 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /components/StyledText.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Text } from 'react-native' 3 | 4 | export class MonoText extends React.Component { 5 | render() { 6 | return 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /components/TabBarIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Icon } from 'expo' 3 | 4 | import Colors from '../constants/Colors' 5 | 6 | export default class TabBarIcon extends React.Component { 7 | render() { 8 | return ( 9 | 15 | ) 16 | } 17 | } -------------------------------------------------------------------------------- /constants/Colors.js: -------------------------------------------------------------------------------- 1 | const tintColor = '#2f95dc' 2 | 3 | export default { 4 | tintColor, 5 | tabIconDefault: '#ccc', 6 | tabIconSelected: tintColor, 7 | tabBar: '#fefefe', 8 | errorBackground: 'red', 9 | errorText: '#fff', 10 | warningBackground: '#EAEB5E', 11 | warningText: '#666804', 12 | noticeBackground: tintColor, 13 | noticeText: '#fff', 14 | } 15 | -------------------------------------------------------------------------------- /constants/Layout.js: -------------------------------------------------------------------------------- 1 | import { Dimensions } from 'react-native' 2 | 3 | const width = Dimensions.get('window').width 4 | const height = Dimensions.get('window').height 5 | 6 | export default { 7 | window: { 8 | width, 9 | height, 10 | }, 11 | isSmallDevice: width < 375, 12 | } 13 | -------------------------------------------------------------------------------- /navigation/AppNavigator.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createAppContainer, createSwitchNavigator } from 'react-navigation' 3 | 4 | import MainTabNavigator from './MainTabNavigator' 5 | 6 | export default createAppContainer(createSwitchNavigator({ 7 | // You could add another route here for authentication. 8 | // Read more at https://reactnavigation.org/docs/en/auth-flow.html 9 | Main: MainTabNavigator, 10 | })) -------------------------------------------------------------------------------- /navigation/MainTabNavigator.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/display-name */ 2 | import React from 'react' 3 | import { Platform } from 'react-native' 4 | import { createBottomTabNavigator } from 'react-navigation' 5 | 6 | import TabBarIcon from '../components/TabBarIcon' 7 | import Task0 from '../screens/Task0' 8 | import Task0b from '../screens/Task0b' 9 | import Task1 from '../screens/Task1' 10 | import Task2 from '../screens/Task2' 11 | import Task3 from '../screens/Task3' 12 | import Task4 from '../screens/Task4' 13 | import Task5 from '../screens/Task5' 14 | import Task6 from '../screens/Task6' 15 | 16 | // setInterval(() => { 17 | // let iters = 1e8, sum = 0; 18 | // while (iters-- > 0) sum += iters; 19 | // }, 300); 20 | 21 | const enhance = (Component, name) => { 22 | Component.navigationOptions = { 23 | tabBarLabel: name, 24 | header: null, 25 | tabBarIcon: ({ focused }) => ( 26 | 34 | ), 35 | } 36 | 37 | return Component 38 | } 39 | 40 | export default createBottomTabNavigator({ 41 | Task0: enhance(Task0, 'Task 0'), 42 | Task0b: enhance(Task0b, 'Task 0b'), 43 | Task1: enhance(Task1, 'Task 1'), 44 | Task2: enhance(Task2, 'Task 2'), 45 | Task3: enhance(Task3, 'Task 3'), 46 | Task4: enhance(Task4, 'Task 4'), 47 | Task5: enhance(Task5, 'Task 5'), 48 | Task6: enhance(Task6, 'Task 6'), 49 | }, { 50 | initialRouteName: 'Task0', 51 | }) 52 | -------------------------------------------------------------------------------- /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 | "eject": "expo eject", 8 | "test": "node ./node_modules/jest/bin/jest.js --watchAll" 9 | }, 10 | "jest": { 11 | "preset": "jest-expo" 12 | }, 13 | "dependencies": { 14 | "@expo/samples": "2.1.1", 15 | "expo": "^32.0.0", 16 | "react": "16.5.0", 17 | "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz", 18 | "react-navigation": "^3.0.9" 19 | }, 20 | "devDependencies": { 21 | "babel-eslint": "^10.0.1", 22 | "babel-preset-expo": "^5.0.0", 23 | "eslint": "^5.15.1", 24 | "eslint-plugin-react": "^7.12.4", 25 | "jest-expo": "^32.0.0" 26 | }, 27 | "private": true 28 | } 29 | -------------------------------------------------------------------------------- /screens/ClientSays.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React from 'react' 3 | import { TouchableOpacity, View } from 'react-native' 4 | import { Icon } from 'expo' 5 | import { MonoText } from '../components/StyledText' 6 | 7 | export class CliectSays extends React.Component { 8 | state = { 9 | text: '' 10 | }; 11 | 12 | onPress = () => { 13 | if (this.props.text.length === this.state.text.length) { 14 | this.setState({ 15 | text: '' 16 | }) 17 | return 18 | } 19 | this.timer = setInterval(() => { 20 | if (this.props.text.length === this.state.text.length) { 21 | clearInterval(this.timer) 22 | return 23 | } 24 | this.setState(prev => ({ 25 | text: this.props.text.substring(0, prev.text.length + 1) 26 | })) 27 | }, 30) 28 | }; 29 | 30 | componentWillUnmount() { 31 | clearInterval(this.timer) 32 | } 33 | 34 | render() { 35 | return ( 36 | 48 | 49 | {!!this.state.text.length && 56 | 57 | {this.state.text} 58 | 59 | 60 | } 61 | 62 | ) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /screens/LinksScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ScrollView, StyleSheet } from 'react-native' 3 | import { ExpoLinksView } from '@expo/samples' 4 | 5 | export default class LinksScreen extends React.Component { 6 | static navigationOptions = { 7 | title: 'Links', 8 | }; 9 | 10 | render() { 11 | return ( 12 | 13 | {/* Go ahead and delete ExpoLinksView and replace it with your 14 | * content, we just wanted to provide you with some helpful links */} 15 | 16 | 17 | ) 18 | } 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | flex: 1, 24 | paddingTop: 15, 25 | backgroundColor: '#fff', 26 | }, 27 | }) 28 | -------------------------------------------------------------------------------- /screens/SettingsScreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ExpoConfigView } from '@expo/samples' 3 | 4 | export default class SettingsScreen extends React.Component { 5 | static navigationOptions = { 6 | title: 'app.json', 7 | }; 8 | 9 | render() { 10 | /* Go ahead and delete ExpoConfigView and replace it with your 11 | * content, we just wanted to give you a quick view of your config */ 12 | return 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /screens/Task0.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Animated, StyleSheet, View, ScrollView } from 'react-native' 3 | import { GestureHandler } from 'expo' 4 | import { CliectSays } from './ClientSays' 5 | import { MonoText } from '../components/StyledText' 6 | 7 | const { PanGestureHandler, State } = GestureHandler 8 | export default class Task0 extends React.Component { 9 | render() { 10 | return ( 11 | 12 | 13 | 14 | At vero eos et accusamus et iusto odio dignissimos 15 | ducimus qui blanditiis praesentium voluptatum deleniti 16 | atque corrupti quos dolores et quas molestias excepturi 17 | sint occaecati cupiditate non provident, similique 18 | sunt in culpa qui officia deserunt mollitia animi, 19 | id est laborum et dolorum fuga. Et harum quidem 20 | rerum facilis est et expedita distinctio. Nam libero 21 | tempore, cum soluta nobis est eligendi optio cumque 22 | nihil impedit quo minus id quod maxime placeat facere 23 | possimus, omnis voluptas assumenda est, omnis dolor 24 | repellendus. Temporibus autem quibusdam et aut officiis 25 | debitis aut rerum necessitatibus saepe eveniet ut et 26 | voluptates repudiandae sint et molestiae non recusandae. 27 | Itaque earum rerum hic tenetur a sapiente delectus, ut aut 28 | reiciendis voluptatibus maiores alias consequatur aut 29 | perferendis doloribus asperiores repellat. 30 | At vero eos et accusamus et iusto odio dignissimos 31 | ducimus qui blanditiis praesentium voluptatum deleniti 32 | atque corrupti quos dolores et quas molestias excepturi 33 | sint occaecati cupiditate non provident, similique 34 | sunt in culpa qui officia deserunt mollitia animi, 35 | id est laborum et dolorum fuga. Et harum quidem 36 | rerum facilis est et expedita distinctio. Nam libero 37 | tempore, cum soluta nobis est eligendi optio cumque 38 | nihil impedit quo minus id quod maxime placeat facere 39 | possimus, omnis voluptas assumenda est, omnis dolor 40 | repellendus. Temporibus autem quibusdam et aut officiis 41 | debitis aut rerum necessitatibus saepe eveniet ut et 42 | voluptates repudiandae sint et molestiae non recusandae. 43 | Itaque earum rerum hic tenetur a sapiente delectus, ut aut 44 | reiciendis voluptatibus maiores alias consequatur aut 45 | perferendis doloribus asperiores repellat. 46 | 47 | 48 | 49 | 50 | ) 51 | } 52 | } 53 | 54 | const styles = StyleSheet.create({ 55 | container: { 56 | flex: 1, 57 | backgroundColor: '#fff', 58 | justifyContent: 'center', 59 | }, 60 | box: { 61 | width: 100, 62 | backgroundColor: 'yellow', 63 | height: 100, 64 | } 65 | }) 66 | -------------------------------------------------------------------------------- /screens/Task0b.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Animated, StyleSheet, View, ScrollView, Image } from 'react-native' 3 | import { GestureHandler } from 'expo' 4 | import { CliectSays } from './ClientSays' 5 | import { MonoText } from '../components/StyledText' 6 | 7 | const AniamtedImage = props => ( 8 | 21 | ) 22 | 23 | const Lorem = () => ( 24 | 29 | 30 | At vero eos et accusamus et iusto odio dignissimos 31 | ducimus qui blanditiis praesentium voluptatum deleniti 32 | atque corrupti quos dolores et quas molestias excepturi 33 | sint occaecati cupiditate non provident, similique 34 | sunt in culpa qui officia deserunt mollitia animi, 35 | id est laborum et dolorum fuga. Et harum quidem 36 | rerum facilis est et expedita distinctio. Nam libero 37 | tempore, cum soluta nobis est eligendi optio cumque 38 | nihil impedit quo minus id quod maxime placeat facere 39 | possimus, omnis voluptas assumenda est, omnis dolor 40 | repellendus. Temporibus autem quibusdam et aut officiis 41 | debitis aut rerum necessitatibus saepe eveniet ut et 42 | voluptates repudiandae sint et molestiae non recusandae. 43 | Itaque earum rerum hic tenetur a sapiente delectus, ut aut 44 | reiciendis voluptatibus maiores alias consequatur aut 45 | perferendis doloribus asperiores repellat. 46 | At vero eos et accusamus et iusto odio dignissimos 47 | ducimus qui blanditiis praesentium voluptatum deleniti 48 | atque corrupti quos dolores et quas molestias excepturi 49 | sint occaecati cupiditate non provident, similique 50 | sunt in culpa qui officia deserunt mollitia animi, 51 | id est laborum et dolorum fuga. Et harum quidem 52 | rerum facilis est et expedita distinctio. Nam libero 53 | tempore, cum soluta nobis est eligendi optio cumque 54 | nihil impedit quo minus id quod maxime placeat facere 55 | possimus, omnis voluptas assumenda est, omnis dolor 56 | repellendus. Temporibus autem quibusdam et aut officiis 57 | debitis aut rerum necessitatibus saepe eveniet ut et 58 | voluptates repudiandae sint et molestiae non recusandae. 59 | Itaque earum rerum hic tenetur a sapiente delectus, ut aut 60 | reiciendis voluptatibus maiores alias consequatur aut 61 | perferendis doloribus asperiores repellat. 62 | 63 | 64 | ) 65 | 66 | const Header = () => ( 67 | 74 | 75 | Header 76 | 77 | 78 | ) 79 | 80 | const { PanGestureHandler, State } = GestureHandler 81 | export default class Task0 extends React.Component { 82 | trans = new Animated.Value(0) 83 | int = this.trans.interpolate({ 84 | inputRange: [0, 100, 110], 85 | outputRange: [0, 50, 50], 86 | }) 87 | render() { 88 | return ( 89 | 90 | 93 |
94 | 95 |
96 | 97 |
98 | 99 | 100 | 101 | 102 | ) 103 | } 104 | } 105 | 106 | const styles = StyleSheet.create({ 107 | container: { 108 | flex: 1, 109 | backgroundColor: '#fff', 110 | justifyContent: 'center', 111 | marginTop: 16 112 | }, 113 | box: { 114 | width: 100, 115 | backgroundColor: 'yellow', 116 | height: 100, 117 | } 118 | }) 119 | -------------------------------------------------------------------------------- /screens/Task1.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Animated, StyleSheet, View, } from 'react-native' 3 | import { GestureHandler } from 'expo' 4 | import { CliectSays } from './ClientSays' 5 | 6 | const { PanGestureHandler, State } = GestureHandler 7 | export default class Task1 extends React.Component { 8 | render() { 9 | return ( 10 | 11 | 16 | 17 | 18 | ) 19 | } 20 | } 21 | 22 | const styles = StyleSheet.create({ 23 | container: { 24 | flex: 1, 25 | backgroundColor: '#fff', 26 | justifyContent: 'center', 27 | alignItems: 'center' 28 | }, 29 | box: { 30 | width: 100, 31 | backgroundColor: 'yellow', 32 | height: 100, 33 | } 34 | }) 35 | -------------------------------------------------------------------------------- /screens/Task2.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Animated, StyleSheet, View, } from 'react-native' 3 | import { GestureHandler } from 'expo' 4 | import { CliectSays } from './ClientSays' 5 | 6 | const { PanGestureHandler, State, PinchGestureHandler, RotationGestureHandler } = GestureHandler 7 | 8 | 9 | export default class Task1 extends React.Component { 10 | translateX = new Animated.Value(0); 11 | translateY = new Animated.Value(0); 12 | prevX = 0; 13 | prevY = 0; 14 | 15 | onPanGestureEvent = Animated.event( 16 | [ 17 | { 18 | nativeEvent: { 19 | translationX: this.translateX, 20 | translationY: this.translateY, 21 | }, 22 | }, 23 | ], 24 | { useNativeDriver: true } 25 | ); 26 | 27 | onPanHandlerStateChange = ({ nativeEvent: { oldState, translationX, translationY } }) => { 28 | if (oldState === State.ACTIVE) { 29 | this.prevX += translationX 30 | this.prevY += translationY 31 | this.translateX.setValue(0) 32 | this.translateY.setValue(0) 33 | this.translateX.setOffset(this.prevX) 34 | this.translateY.setOffset(this.prevY) 35 | } 36 | }; 37 | 38 | render() { 39 | return ( 40 | 41 | 46 | 51 | 52 | 55 | 56 | 59 | 60 | 63 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | ) 82 | } 83 | } 84 | 85 | const styles = StyleSheet.create({ 86 | container: { 87 | flex: 1, 88 | backgroundColor: '#fff', 89 | justifyContent: 'center', 90 | alignItems: 'center' 91 | }, 92 | box: { 93 | width: 100, 94 | backgroundColor: 'yellow', 95 | height: 100, 96 | }, 97 | }) 98 | -------------------------------------------------------------------------------- /screens/Task3.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Animated, StyleSheet, View, } from 'react-native' 3 | import { GestureHandler } from 'expo' 4 | import { CliectSays } from './ClientSays' 5 | 6 | const { PanGestureHandler, State, PinchGestureHandler, RotationGestureHandler } = GestureHandler 7 | 8 | 9 | export default class Task1 extends React.Component { 10 | translateX = new Animated.Value(0); 11 | translateY = new Animated.Value(0); 12 | prevX = 0; 13 | prevY = 0; 14 | 15 | onPanGestureEvent = Animated.event( 16 | [ 17 | { 18 | nativeEvent: { 19 | translationX: this.translateX, 20 | translationY: this.translateY, 21 | }, 22 | }, 23 | ], 24 | { useNativeDriver: true } 25 | ); 26 | 27 | onPanHandlerStateChange = ({ nativeEvent: { oldState, translationX, translationY } }) => { 28 | if (oldState === State.ACTIVE) { 29 | this.prevX += translationX 30 | this.prevY += translationY 31 | this.translateX.setValue(0) 32 | this.translateY.setValue(0) 33 | this.translateX.setOffset(this.prevX) 34 | this.translateY.setOffset(this.prevY) 35 | } 36 | }; 37 | 38 | baseScale = new Animated.Value(1); 39 | pinchScale = new Animated.Value(1); 40 | scale = Animated.multiply(this.baseScale, this.pinchScale); 41 | lastScale = 1; 42 | onPinchGestureEvent = Animated.event( 43 | [{ nativeEvent: { scale: this.pinchScale } }], 44 | { useNativeDriver: true } 45 | ); 46 | 47 | onPinchHandlerStateChange = event => { 48 | if (event.nativeEvent.oldState === State.ACTIVE) { 49 | this.lastScale *= event.nativeEvent.scale 50 | this.baseScale.setValue(this.lastScale) 51 | this.pinchScale.setValue(1) 52 | } 53 | }; 54 | 55 | baseRotation = new Animated.Value(0); 56 | zoomRotation = new Animated.Value(0); 57 | rotation = Animated.add(this.baseRotation, this.zoomRotation); 58 | lastRotation = 0; 59 | onRotationGestureEvent = Animated.event( 60 | [{ nativeEvent: { rotation: this.zoomRotation } }], 61 | { useNativeDriver: true } 62 | ); 63 | 64 | rotationString = this.rotation.interpolate({ 65 | inputRange: [-100, 100], 66 | outputRange: ['-100rad', '100rad'], 67 | }); 68 | 69 | onRotationHandlerStateChange = event => { 70 | if (event.nativeEvent.oldState === State.ACTIVE) { 71 | this.lastRotation += event.nativeEvent.rotation 72 | this.baseRotation.setValue(this.lastRotation) 73 | this.zoomRotation.setValue(0) 74 | } 75 | }; 76 | 77 | pinch = React.createRef(); 78 | pan = React.createRef(); 79 | rotate = React.createRef(); 80 | 81 | render() { 82 | return ( 83 | 84 | 89 | 96 | 97 | 100 | 105 | 108 | 113 | 116 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | ) 137 | } 138 | } 139 | 140 | const styles = StyleSheet.create({ 141 | container: { 142 | flex: 1, 143 | backgroundColor: '#fff', 144 | justifyContent: 'center', 145 | alignItems: 'center' 146 | }, 147 | box: { 148 | width: 100, 149 | backgroundColor: 'yellow', 150 | height: 100, 151 | }, 152 | }) 153 | -------------------------------------------------------------------------------- /screens/Task4.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { StyleSheet, View } from 'react-native' 3 | import { DangerZone, GestureHandler } from 'expo' 4 | import { CliectSays } from './ClientSays' 5 | 6 | const { Animated } = DangerZone 7 | const { 8 | PanGestureHandler, 9 | PinchGestureHandler, 10 | State, 11 | } = GestureHandler 12 | 13 | const { set, Easing, timing, cond, block, eq, add, Value, sub,event, diff, multiply, clockRunning, startClock, stopClock, decay, Clock } = Animated 14 | 15 | function runTiming(clock, value, dest) { 16 | const state = { 17 | finished: new Value(0), 18 | position: new Value(0), 19 | frameTime: new Value(0), 20 | time: new Value(0), 21 | }; 22 | 23 | const config = { 24 | toValue: new Value(0), 25 | duration: 300, 26 | easing: Easing.inOut(Easing.cubic), 27 | }; 28 | 29 | return [ 30 | cond(clockRunning(clock), 0, [ 31 | set(state.finished, 0), 32 | set(state.frameTime, 0), 33 | set(state.time, 0), 34 | set(state.position, value), 35 | set(config.toValue, dest), 36 | startClock(clock), 37 | ]), 38 | timing(clock, state, config), 39 | cond(state.finished, stopClock(clock)), 40 | state.position, 41 | ]; 42 | } 43 | 44 | function withPreservingOffset(drag, state) { 45 | const prev = new Animated.Value(0) 46 | const valWithPreservedOffset = new Animated.Value(0) 47 | return block([ 48 | cond(eq(state, State.BEGAN), [ 49 | set(prev, 0) 50 | ], [ 51 | set(valWithPreservedOffset, add(valWithPreservedOffset, sub(drag, prev))), 52 | set(prev, drag), 53 | ]), 54 | valWithPreservedOffset 55 | ]) 56 | } 57 | 58 | function runDecay(clock, value, velocity) { 59 | const state = { 60 | finished: new Value(0), 61 | velocity: new Value(0), 62 | position: new Value(0), 63 | time: new Value(0), 64 | } 65 | const config = { deceleration: 0.99 } 66 | return [ 67 | cond(clockRunning(clock), 0, [ 68 | set(state.finished, 0), 69 | set(state.velocity, velocity), 70 | set(state.position, value), 71 | set(state.time, 0), 72 | startClock(clock), 73 | ]), 74 | decay(clock, state, config), 75 | cond(state.finished, stopClock(clock)), 76 | state.position, 77 | ] 78 | } 79 | 80 | export default class Example extends Component { 81 | constructor(props) { 82 | super(props) 83 | this.Y = new Value(0) 84 | this.R = new Value(0) 85 | this.Z = new Value(1) 86 | const prevZ = new Value(1) 87 | const dragX = new Value(0) 88 | const dragY = new Value(0) 89 | const panState = new Value(0) 90 | 91 | 92 | this.handlePan = event([ 93 | { 94 | nativeEvent: ({ 95 | translationX: dragX, 96 | translationY: dragY, 97 | state: panState 98 | }) 99 | }, 100 | ]) 101 | 102 | this.handleZoom = event([ 103 | { 104 | nativeEvent: ({ scale: z, state }) => 105 | block([ 106 | cond(eq(state, State.ACTIVE), set(this.Z, multiply(z, prevZ))), 107 | cond(eq(state, State.END), [set(prevZ, this.Z)]), 108 | ]), 109 | }, 110 | ]) 111 | 112 | this.X = withPreservingOffset(dragX, panState) 113 | this.Y = withPreservingOffset(dragY, panState) 114 | } 115 | 116 | panRef = React.createRef(); 117 | pinchRef = React.createRef(); 118 | 119 | render() { 120 | return ( 121 | 122 | 129 | 135 | 138 | 143 | 146 | 147 | 161 | 162 | 163 | 164 | 165 | 166 | ) 167 | } 168 | } 169 | 170 | const IMAGE_SIZE = 200 171 | 172 | const styles = StyleSheet.create({ 173 | container: { 174 | flex: 1, 175 | backgroundColor: '#F5FCFF', 176 | justifyContent: 'center', 177 | alignItems: 'center', 178 | }, 179 | box: { 180 | width: IMAGE_SIZE, 181 | height: IMAGE_SIZE, 182 | }, 183 | }) 184 | 185 | -------------------------------------------------------------------------------- /screens/Task5.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { StyleSheet, View } from 'react-native' 3 | import { DangerZone, GestureHandler } from 'expo' 4 | import { CliectSays } from './ClientSays' 5 | 6 | const { Animated } = DangerZone 7 | const { 8 | PanGestureHandler, 9 | PinchGestureHandler, 10 | State, 11 | } = GestureHandler 12 | 13 | const { set, cond, block, eq, add, Value, divide, greaterThan, sub,event, diff, multiply, clockRunning, startClock, stopClock, decay, Clock, lessThan } = Animated 14 | 15 | function runDecay(clock, value, velocity, wasStartedFromBegin) { 16 | const state = { 17 | finished: new Value(0), 18 | velocity: new Value(0), 19 | position: new Value(0), 20 | time: new Value(0), 21 | } 22 | 23 | const config = { deceleration: 0.99 } 24 | 25 | return [ 26 | cond(clockRunning(clock), 0, [ 27 | cond(wasStartedFromBegin, 0, [ 28 | set(wasStartedFromBegin, 1), 29 | set(state.finished, 0), 30 | set(state.velocity, velocity), 31 | set(state.position, value), 32 | set(state.time, 0), 33 | startClock(clock), 34 | ]), 35 | ]), 36 | // set(state.position, value), 37 | decay(clock, state, config), 38 | cond(state.finished, stopClock(clock)), 39 | state.position, 40 | ] 41 | } 42 | 43 | function withPreservingMultiplicativeOffset (val, state) { 44 | const prev = new Animated.Value(1) 45 | const valWithPreservedOffset = new Animated.Value(1) 46 | return block([ 47 | cond(eq(state, State.BEGAN), [ 48 | set(prev, 1) 49 | ], [ 50 | set(valWithPreservedOffset, multiply(valWithPreservedOffset, divide(val, prev))), 51 | set(prev, val), 52 | ]), 53 | valWithPreservedOffset 54 | ]) 55 | } 56 | 57 | function withPreservingAdditiveOffset(drag, state) { 58 | const prev = new Animated.Value(0) 59 | const valWithPreservedOffset = new Animated.Value(0) 60 | return block([ 61 | cond(eq(state, State.BEGAN), [ 62 | set(prev, 0) 63 | ], [ 64 | set(valWithPreservedOffset, add(valWithPreservedOffset, sub(drag, prev))), 65 | set(prev, drag), 66 | ]), 67 | valWithPreservedOffset 68 | ]) 69 | } 70 | 71 | function withDecaying(drag, state, velocity) { 72 | const valDecayed = new Animated.Value(0) 73 | const offset = new Animated.Value(0) 74 | const decayClock = new Clock() 75 | // since there might be moar than one clock 76 | const wasStartedFromBegin = new Animated.Value(0) 77 | return block([ 78 | cond(eq(state, State.END), 79 | [ 80 | set(valDecayed, runDecay(decayClock, add(drag, offset), velocity, wasStartedFromBegin)) 81 | ], 82 | [ 83 | stopClock(decayClock), 84 | cond(eq(state, State.BEGAN), [ 85 | set(wasStartedFromBegin, 0), 86 | set(offset, add(sub(valDecayed, drag))) 87 | ]), 88 | set(valDecayed, add(drag, offset)) 89 | 90 | ], 91 | ), 92 | valDecayed, 93 | ]) 94 | } 95 | 96 | 97 | export default class Example extends Component { 98 | constructor(props) { 99 | super(props) 100 | const dragX = new Value(0) 101 | const dragY = new Value(0) 102 | const scale = new Value(1) 103 | const panState = new Value(0) 104 | const scaleState = new Value(0) 105 | const velocityX = new Value(0) 106 | const velocityY = new Value(0) 107 | 108 | 109 | this.handlePan = event([ 110 | { 111 | nativeEvent: ({ 112 | translationX: dragX, 113 | translationY: dragY, 114 | state: panState, 115 | velocityY, 116 | velocityX 117 | }) 118 | }, 119 | ]) 120 | 121 | this.handleZoom = event([ 122 | { 123 | nativeEvent: { 124 | scale, 125 | state: scaleState 126 | } 127 | }, 128 | ]) 129 | 130 | this.X = withDecaying(withPreservingAdditiveOffset(dragX, panState), panState, velocityX) 131 | this.Y = withDecaying(withPreservingAdditiveOffset(dragY, panState), panState, velocityY) 132 | this.scale = withPreservingMultiplicativeOffset(scale, scaleState) 133 | } 134 | 135 | panRef = React.createRef(); 136 | pinchRef = React.createRef(); 137 | 138 | render() { 139 | return ( 140 | 141 | 148 | 153 | 156 | 161 | 164 | 178 | 179 | 180 | 181 | 182 | 183 | ) 184 | } 185 | } 186 | 187 | const IMAGE_SIZE = 200 188 | 189 | const styles = StyleSheet.create({ 190 | container: { 191 | flex: 1, 192 | backgroundColor: '#F5FCFF', 193 | justifyContent: 'center', 194 | alignItems: 'center', 195 | }, 196 | box: { 197 | width: IMAGE_SIZE, 198 | height: IMAGE_SIZE, 199 | }, 200 | }) 201 | 202 | -------------------------------------------------------------------------------- /screens/Task6.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { StyleSheet, View } from 'react-native' 3 | import { DangerZone, GestureHandler } from 'expo' 4 | import { CliectSays } from './ClientSays' 5 | 6 | const { Animated } = DangerZone 7 | const { 8 | PanGestureHandler, 9 | PinchGestureHandler, 10 | State, 11 | } = GestureHandler 12 | 13 | const { set, cond, block, eq, add, and, sqrt, Value, spring, or, divide, greaterThan, sub,event, diff, multiply, clockRunning, startClock, stopClock, decay, Clock, lessThan } = Animated 14 | 15 | 16 | function runDecay(clock, value, velocity, wasStartedFromBegin) { 17 | const state = { 18 | finished: new Value(0), 19 | velocity: new Value(0), 20 | position: new Value(0), 21 | time: new Value(0), 22 | } 23 | 24 | const config = { deceleration: 0.99 } 25 | 26 | return [ 27 | cond(clockRunning(clock), 0, [ 28 | cond(wasStartedFromBegin, 0, [ 29 | set(wasStartedFromBegin, 1), 30 | set(state.finished, 0), 31 | set(state.velocity, velocity), 32 | set(state.position, value), 33 | set(state.time, 0), 34 | startClock(clock), 35 | ]), 36 | ]), 37 | // set(state.position, value), 38 | decay(clock, state, config), 39 | cond(state.finished, stopClock(clock)), 40 | state.position, 41 | ] 42 | } 43 | 44 | function withPreservingMultiplicativeOffset (val, state) { 45 | const prev = new Animated.Value(1) 46 | const valWithPreservedOffset = new Animated.Value(1) 47 | return block([ 48 | cond(eq(state, State.BEGAN), [ 49 | set(prev, 1) 50 | ], [ 51 | set(valWithPreservedOffset, multiply(valWithPreservedOffset, divide(val, prev))), 52 | set(prev, val), 53 | ]), 54 | valWithPreservedOffset 55 | ]) 56 | } 57 | 58 | function withPreservingAdditiveOffset(drag, state) { 59 | const prev = new Animated.Value(0) 60 | const valWithPreservedOffset = new Animated.Value(0) 61 | return block([ 62 | cond(eq(state, State.BEGAN), [ 63 | set(prev, 0) 64 | ], [ 65 | set(valWithPreservedOffset, add(valWithPreservedOffset, sub(drag, prev))), 66 | set(prev, drag), 67 | ]), 68 | valWithPreservedOffset 69 | ]) 70 | } 71 | 72 | function withDecaying(drag, state, velocity) { 73 | const valDecayed = new Animated.Value(0) 74 | const offset = new Animated.Value(0) 75 | const decayClock = new Clock() 76 | // since there might be moar than one clock 77 | const wasStartedFromBegin = new Animated.Value(0) 78 | return block([ 79 | cond(eq(state, State.END), 80 | [ 81 | set(valDecayed, runDecay(decayClock, add(drag, offset), velocity, wasStartedFromBegin)) 82 | ], 83 | [ 84 | stopClock(decayClock), 85 | cond(eq(state, State.BEGAN), [ 86 | set(wasStartedFromBegin, 0), 87 | set(offset, add(sub(valDecayed, drag))) 88 | ]), 89 | set(valDecayed, add(drag, offset)) 90 | 91 | ], 92 | ), 93 | valDecayed, 94 | ]) 95 | } 96 | 97 | 98 | function runSpring(clock, value, velocity, dest) { 99 | const state = { 100 | finished: new Value(0), 101 | velocity: new Value(0), 102 | position: new Value(0), 103 | time: new Value(0), 104 | } 105 | 106 | const config = { 107 | damping: 7, 108 | mass: 1, 109 | stiffness: 121.6, 110 | overshootClamping: false, 111 | restSpeedThreshold: 0.001, 112 | restDisplacementThreshold: 0.001, 113 | toValue: new Value(0), 114 | } 115 | 116 | return [ 117 | cond(clockRunning(clock), 0, [ 118 | set(state.finished, 0), 119 | set(state.velocity, velocity), 120 | set(state.position, value), 121 | set(config.toValue, dest), 122 | startClock(clock), 123 | ]), 124 | spring(clock, state, config), 125 | cond(state.finished, stopClock(clock)), 126 | state.position, 127 | ] 128 | } 129 | 130 | 131 | function withLimits(val, min, max, state) { 132 | const offset = new Animated.Value(0) 133 | const offsetedVal = add(offset, val) 134 | return block([ 135 | cond(eq(state, State.BEGAN),[ 136 | cond(lessThan(offsetedVal, min), 137 | set(offset, sub(min, val))), 138 | cond(greaterThan(offsetedVal, max), 139 | set(offset, sub(max, val))) 140 | ]), 141 | cond(lessThan(offsetedVal, min), min, cond(greaterThan(offsetedVal, max), max, offsetedVal)) 142 | ]) 143 | } 144 | 145 | export default class Example extends Component { 146 | constructor(props) { 147 | super(props) 148 | const dragX = new Value(0) 149 | const dragY = new Value(0) 150 | const scale = new Value(1) 151 | const panState = new Value(0) 152 | const scaleState = new Value(0) 153 | const velocityX = new Value(0) 154 | const velocityY = new Value(0) 155 | 156 | 157 | 158 | this.handlePan = event([ 159 | { 160 | nativeEvent: ({ 161 | translationX: dragX, 162 | translationY: dragY, 163 | state: panState, 164 | velocityY, 165 | velocityX 166 | }) 167 | }, 168 | ]) 169 | 170 | this.handleZoom = event([ 171 | { 172 | nativeEvent: { 173 | scale, 174 | state: scaleState 175 | } 176 | }, 177 | ]) 178 | 179 | this.X = withLimits(withDecaying(withPreservingAdditiveOffset(dragX, panState), panState, velocityX), -100, 100, panState) 180 | this.Y = withLimits(withDecaying(withPreservingAdditiveOffset(dragY, panState), panState, velocityY), -100, 100, panState) 181 | this.scale = withLimits(withPreservingMultiplicativeOffset(scale, scaleState), 0.1, 2, scaleState) 182 | } 183 | 184 | panRef = React.createRef(); 185 | pinchRef = React.createRef(); 186 | 187 | render() { 188 | return ( 189 | 190 | 197 | 202 | 205 | 210 | 213 | 227 | 228 | 229 | 230 | 231 | 232 | ) 233 | } 234 | } 235 | 236 | const IMAGE_SIZE = 200 237 | 238 | const styles = StyleSheet.create({ 239 | container: { 240 | flex: 1, 241 | backgroundColor: '#F5FCFF', 242 | justifyContent: 'center', 243 | alignItems: 'center', 244 | }, 245 | box: { 246 | width: IMAGE_SIZE, 247 | height: IMAGE_SIZE, 248 | }, 249 | }) 250 | 251 | -------------------------------------------------------------------------------- /screens/react-hexagon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osdnk/appjs-workshop/478cb3dadb2504b9a6b0bfe25b90a1ae75b2d0f0/screens/react-hexagon.png --------------------------------------------------------------------------------