├── README.md ├── actions.js ├── animator.js ├── card.js ├── index.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | ## Swiper 2 | 3 | This is one of our open source screens from [React Screens](https://gumroad.com/l/prereactscreens). If you like the work we do, consider subscribing :) 4 | 5 | Read [the blog post](https://zach.codes/building-a-deck-swiper-in-react-native) on how this was made if you need any help. 6 | 7 | ## Preview 8 | 9 | Scan the QR code with [Expo](https://expo.io), or [click here](https://exp.host/@zackify/swiper) if on your device to play around with a demo. 10 | 11 | ![QR code](http://api.qrserver.com/v1/create-qr-code/?color=000000&bgcolor=FFFFFF&data=https%3A%2F%2Fexp.host%2F%40zackify%2Fswiper&qzone=1&margin=0&size=150x150&ecc=L) 12 | 13 | ## Install 14 | 15 | ``` 16 | npm install @reactscreens/swiper --save 17 | ``` 18 | 19 | ### Swiper 20 | 21 | ```js 22 | import Swiper from '@reactscreens/swiper'; 23 | 24 | //Use our default card and actions if you like 25 | import Card from '@reactscreens/swiper/card'; 26 | import Actions from '@reactscreens/swiper/actions'; 27 | 28 | 29 | ... 30 | 31 | export default () => ( 32 | console.log(card, 'tossed left')} 34 | onTossRight={card => console.log(card, 'tossed right')} 35 | actionsBar={toss => } 36 | > 37 | 42 | 47 | 52 | 53 | ) 54 | ``` 55 | 56 | ### Performance 57 | 58 | Currently all children are rendered initially, so don't try rendering a huge list of cards at a time. 59 | 60 | More information coming soon! Yes, we need tests and more performance improvements :) 61 | -------------------------------------------------------------------------------- /actions.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'; 3 | 4 | export default ({ toss }) => { 5 | return ( 6 | 7 | 8 | toss('left')} 11 | > 12 | Dislike 13 | 14 | 15 | 16 | 17 | toss('right')} 20 | > 21 | Like 22 | 23 | 24 | 25 | ); 26 | }; 27 | 28 | const styles = StyleSheet.create({ 29 | actionBar: { 30 | height: 50, 31 | position: 'absolute', 32 | bottom: 10, 33 | width: '100%', 34 | flexDirection: 'row', 35 | justifyContent: 'space-around', 36 | paddingLeft: 13, 37 | paddingRight: 13, 38 | }, 39 | buttonDislikeC: { 40 | height: 75, 41 | width: 75, 42 | borderColor: '#f6f6f6', 43 | borderWidth: 2, 44 | borderRadius: 75, 45 | alignItems: 'center', 46 | justifyContent: 'center', 47 | }, 48 | buttonTextDislike: { 49 | alignItems: 'center', 50 | justifyContent: 'center', 51 | color: 'red', 52 | }, 53 | buttonLikeC: { 54 | height: 75, 55 | width: 75, 56 | borderColor: '#f6f6f6', 57 | borderWidth: 2, 58 | borderRadius: 75, 59 | alignItems: 'center', 60 | justifyContent: 'center', 61 | }, 62 | buttonTextLike: { 63 | alignItems: 'center', 64 | justifyContent: 'center', 65 | color: 'green', 66 | }, 67 | }); 68 | -------------------------------------------------------------------------------- /animator.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { StyleSheet, PanResponder, Animated, Dimensions } from 'react-native'; 3 | 4 | const { width, height } = Dimensions.get('window'); 5 | 6 | export default class Animator extends Component { 7 | componentWillMount() { 8 | let { onTossRight, onTossLeft } = this.props; 9 | this.pan = new Animated.ValueXY(); 10 | 11 | this.cardPanResponder = PanResponder.create({ 12 | onStartShouldSetPanResponder: () => true, 13 | onPanResponderMove: Animated.event([ 14 | null, 15 | { dx: this.pan.x, dy: this.pan.y }, 16 | ]), 17 | onPanResponderRelease: (e, { dx }) => { 18 | const absDx = Math.abs(dx); 19 | const direction = absDx / dx; 20 | 21 | if (absDx > 120) { 22 | Animated.decay(this.pan, { 23 | velocity: { x: 3 * direction, y: 0 }, 24 | deceleration: 0.995, 25 | }).start(dx > 0 ? onTossRight : onTossLeft); 26 | } else { 27 | Animated.spring(this.pan, { 28 | toValue: { x: 0, y: 0 }, 29 | friction: 4.5, 30 | }).start(); 31 | } 32 | }, 33 | }); 34 | } 35 | 36 | componentWillReceiveProps({ toss, onTossRight, onTossLeft }) { 37 | if (toss && !this.props.toss) { 38 | if (toss === 'left') { 39 | return Animated.timing(this.pan, { 40 | toValue: { x: 3 * -180, y: 0 }, 41 | duration: 400, 42 | }).start(onTossLeft); 43 | } 44 | 45 | return Animated.timing(this.pan, { 46 | toValue: { x: 3 * 180, y: 0 }, 47 | duration: 400, 48 | }).start(onTossRight); 49 | } 50 | } 51 | 52 | render() { 53 | let { children, style } = this.props; 54 | const rotateCard = this.pan.x.interpolate({ 55 | inputRange: [-200, 0, 200], 56 | outputRange: ['10deg', '0deg', '-10deg'], 57 | }); 58 | const animatedStyle = { 59 | transform: [ 60 | { translateX: this.pan.x }, 61 | { translateY: this.pan.y }, 62 | { rotate: rotateCard }, 63 | ], 64 | }; 65 | 66 | return ( 67 | 71 | {children} 72 | 73 | ); 74 | } 75 | } 76 | 77 | const styles = StyleSheet.create({ 78 | card: { 79 | position: 'absolute', 80 | width: width - 20, 81 | height: height * 0.7, 82 | top: 20, 83 | margin: 10, 84 | }, 85 | }); 86 | -------------------------------------------------------------------------------- /card.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StyleSheet, View, Image, Text } from 'react-native'; 3 | 4 | export default ({ image, title, subTitle }) => ( 5 | 6 | 7 | 8 | {title} 9 | {subTitle} 10 | 11 | 12 | 13 | ); 14 | 15 | const styles = StyleSheet.create({ 16 | container: { 17 | flex: 1, 18 | borderRadius: 8, 19 | overflow: 'hidden', 20 | backgroundColor: 'white', 21 | }, 22 | textContainer: { 23 | backgroundColor: 'transparent', 24 | alignItems: 'center', 25 | position: 'absolute', 26 | left: 0, 27 | bottom: 0, 28 | marginLeft: 10, 29 | paddingBottom: 10, 30 | alignItems: 'flex-start', 31 | }, 32 | image: { 33 | flex: 1, 34 | }, 35 | text: { 36 | margin: 20, 37 | }, 38 | name: { 39 | fontSize: 20, 40 | color: 'white', 41 | }, 42 | subTitle: { 43 | fontSize: 15, 44 | color: 'white', 45 | }, 46 | }); 47 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { View } from 'react-native'; 3 | import Animator from './animator'; 4 | 5 | export default class Container extends React.Component { 6 | constructor({ children }) { 7 | super(); 8 | this.state = { stack: children }; 9 | } 10 | 11 | onToss(callback) { 12 | let stack = [...this.state.stack]; 13 | this.setState( 14 | { 15 | stack: stack.filter((item, index) => index !== stack.length - 1), 16 | toss: false, 17 | }, 18 | callback 19 | ); 20 | } 21 | 22 | componentWillReceiveProps({ children }) { 23 | if (children !== this.props.children) this.setState({ stack: children }); 24 | } 25 | 26 | render() { 27 | let { stack, toss } = this.state; 28 | let { onTossLeft, onTossRight, actionsBar } = this.props; 29 | 30 | return ( 31 | 32 | {React.Children.map(stack, (child, index) => ( 33 | this.onToss(() => onTossRight(child.props))} 36 | onTossLeft={() => this.onToss(() => onTossLeft(child.props))} 37 | > 38 | {child} 39 | 40 | ))} 41 | {actionsBar(toss => this.setState({ toss }))} 42 | 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@reactscreens/swiper", 3 | "version": "0.0.2", 4 | "description": "Customizable Card Swiper for React Native", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": " " 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+ssh://git@github.com/reactscreens/swiper.git" 12 | }, 13 | "keywords": [ 14 | "react-native", 15 | "swiper", 16 | "deck", 17 | "tinder" 18 | ], 19 | "peerDependencies": { 20 | "react": "^15.0.0 || ^16.0.0", 21 | "react-native": ">= 0.40.0" 22 | }, 23 | "author": "Zach & Dom", 24 | "license": "ISC", 25 | "bugs": { 26 | "url": "https://github.com/reactscreens/swiper/issues" 27 | }, 28 | "homepage": "https://github.com/reactscreens/swiper#readme" 29 | } 30 | --------------------------------------------------------------------------------