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