Code Monkey home page Code Monkey logo

rn-swiper-list's Introduction

rn-swiper-list ⚡️

Simulator.Screen.Recording.-.iPhone.15.Pro.Max.-.2024-05-12.at.17.46.37.mp4

⚡ Lightning fast and customizable tinder-like swiper for React Native

Installation ⚙️

yarn add rn-swiper-list

rn-swiper-list needs react-native-reanimated and react-native-gesture-handler package 💎

yarn add react-native-reanimated react-native-gesture-handler

👇 You also need to complete installations of these packages for more information use the links below 👇

Overview

  • Rotation animation
  • Swipe event callbacks
  • Scale animation
  • Overlay labels
  • Swipe back to previous card with a custom animation
  • More swipe events callbacks
  • Integrating and using a single card with flatlist

Props ✏️

Card Props

Props type description required
data array Array of data objects used to render the cards. Yes
renderCard func(cardData,cardIndex) Function that renders a card based on the provided data and index. Yes
cardStyle object CSS style properties applied to each card. These can be applied inline.
children React.ReactNode Child components to be displayed inside the component. Used typically for composition.

Event callbacks

Props type description default
onSwipeLeft func Function called when a card is swiped left. It receives the index of the card as a parameter. (cardIndex) => {}
onSwipeRight func Function called when a card is swiped right. It receives the index of the card as a parameter. (cardIndex) => {}
onSwipeTop func Function called when a card is swiped top. It receives the index of the card as a parameter. (cardIndex) => {}
onSwipedAll func Function called when all cards have been swiped. () => {}
onSwipeStart func Function called when a swipe event starts. () => {}
onSwipeEnd func Function called when a swipe event ends. () => {}
onSwipeActive func Function called when a swipe event is active. () => {}

Swipe Animation Props

props type description default
disableLeftSwipe bool If true, disables the ability to swipe left. false
disableRightSwipe bool If true, disables the ability to swipe right. false
disableTopSwipe bool If true, disables the ability to swipe upwards. false

Rotation Animation Props

props type description default
translateXRange array Translates the card horizontally. [-windowWidth / 3, 0, windowWidth / 3]
translateYRange array Translates the card vertically. [-windowHeight / 3, 0, windowHeight / 3]
rotateInputRange array Array specifying the range of x values for rotation mapping. [-windowWidth / 3, 0, windowWidth / 3]
outputRotationRange array Array of rotation values corresponding to rotateInputRange. [-Math.PI / 20, 0, Math.PI / 20]

Overlay Labels Animation Props

props type description default
inputOverlayLabelRightOpacityRange array Array defining the input range for animating the opacity of the right overlay label. [0, windowWidth / 3]
outputOverlayLabelRightOpacityRange array Array defining the output opacity values for the right overlay label, corresponding to the input range. [0, 1]
inputOverlayLabelLeftOpacityRange array Array defining the input range for animating the opacity of the left overlay label. [0, -(windowWidth / 3)]
outputOverlayLabelLeftOpacityRange array Array defining the output opacity values for the left overlay label, corresponding to the input range. [0, 1]
inputOverlayLabelTopOpacityRange array Array defining the input range for animating the opacity of the top overlay label. [0, -(windowHeight / 3)]
outputOverlayLabelTopOpacityRange array Array defining the output opacity values for the top overlay label, corresponding to the input range. [0, 1]
OverlayLabelRight () => JSX.Element Component rendered as an overlay label for right swipes.
OverlayLabelLeft () => JSX.Element Component rendered as an overlay label for left swipes.
OverlayLabelTop () => JSX.Element Component rendered as an overlay label for top swipes.

Swipe methods

props type description
swipeBack callback Resets the card position after a swipe event
swipeRight callback Animates the card to fling to the right and calls onSwipeRight
swipeLeft callback Animates the card to fling to the left and calls onSwipeLeft
swipeTop callback Animates the card to fling to the top and calls onSwipeTop

Usage 🧑‍💻

/* eslint-disable react-native/no-inline-styles */
import React, { useCallback, useRef } from 'react';
import {
  Image,
  StyleSheet,
  View,
  type ImageSourcePropType,
} from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { AntDesign } from '@expo/vector-icons';
import { Swiper, type SwiperCardRefType } from 'rn-swiper-list';

import { ActionButton } from '../components';

const IMAGES: ImageSourcePropType[] = [
  require('../assets/images/1.jpg'),
  require('../assets/images/2.jpg'),
  require('../assets/images/3.jpg'),
  require('../assets/images/4.jpg'),
  require('../assets/images/5.jpg'),
  require('../assets/images/6.jpg'),
];

const App = () => {
  const ref = useRef<SwiperCardRefType>();

  const renderCard = useCallback(
    (image: ImageSourcePropType) => {
      return (
        <View style={styles.renderCardContainer}>
          <Image
            source={image}
            style={styles.renderCardImage}
            resizeMode="cover"
          />
        </View>
      );
    },
    []
  );
  const OverlayLabelRight = useCallback(() => {
    return (
      <View
        style={[
          styles.overlayLabelContainer,
          {
            backgroundColor: 'green',
          },
        ]}
      />
    );
  }, []);
  const OverlayLabelLeft = useCallback(() => {
    return (
      <View
        style={[
          styles.overlayLabelContainer,
          {
            backgroundColor: 'red',
          },
        ]}
      />
    );
  }, []);
  const OverlayLabelTop = useCallback(() => {
    return (
      <View
        style={[
          styles.overlayLabelContainer,
          {
            backgroundColor: 'blue',
          },
        ]}
      />
    );
  }, []);

  return (
    <GestureHandlerRootView style={styles.container}>
      <View style={styles.subContainer}>
        <Swiper
          ref={ref}
          cardStyle={styles.cardStyle}
          data={IMAGES}
          renderCard={renderCard}
          onSwipeRight={(cardIndex) => {
            console.log('cardIndex', cardIndex);
          }}
          onSwipedAll={() => {
            console.log('onSwipedAll');
          }}
          onSwipeLeft={(cardIndex) => {
            console.log('onSwipeLeft', cardIndex);
          }}
          onSwipeTop={(cardIndex) => {
            console.log('onSwipeTop', cardIndex);
          }}
          OverlayLabelRight={OverlayLabelRight}
          OverlayLabelLeft={OverlayLabelLeft}
          OverlayLabelTop={OverlayLabelTop}
          onSwipeActive={() => {
            console.log('onSwipeActive');
          }}
          onSwipeStart={() => {
            console.log('onSwipeStart');
          }}
          onSwipeEnd={() => {
            console.log('onSwipeEnd');
          }}
        />
      </View>

      <View style={styles.buttonsContainer}>
        <ActionButton
          style={styles.button}
          onTap={() => {
            ref.current?.swipeLeft();
          }}
        >
          <AntDesign name="close" size={32} color="white" />
        </ActionButton>
        <ActionButton
          style={[styles.button, { height: 60, marginHorizontal: 10 }]}
          onTap={() => {
            ref.current?.swipeBack();
          }}
        >
          <AntDesign name="reload1" size={24} color="white" />
        </ActionButton>
        <ActionButton
          style={styles.button}
          onTap={() => {
            ref.current?.swipeTop();
          }}
        >
          <AntDesign name="arrowup" size={32} color="white" />
        </ActionButton>
        <ActionButton
          style={styles.button}
          onTap={() => {
            ref.current?.swipeRight();
          }}
        >
          <AntDesign name="heart" size={32} color="white" />
        </ActionButton>
      </View>
    </GestureHandlerRootView>
  );
};

export default App;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  buttonsContainer: {
    flexDirection: 'row',
    bottom: 34,
    alignItems: 'center',
    justifyContent: 'center',
  },
  button: {
    height: 80,
    borderRadius: 40,
    marginHorizontal: 20,
    aspectRatio: 1,
    backgroundColor: '#3A3D45',
    elevation: 4,
    justifyContent: 'center',
    alignItems: 'center',
    shadowColor: 'black',
    shadowOpacity: 0.1,
    shadowOffset: {
      width: 0,
      height: 4,
    },
  },
  buttonText: {
    fontSize: 20,
    fontWeight: 'bold',
  },
  cardStyle: {
    width: '95%',
    height: '75%',
    borderRadius: 15,
    marginVertical: 20,
  },
  renderCardContainer: {
    flex: 1,
    borderRadius: 15,
    height: '75%',
    width: '100%',
  },
  renderCardImage: {
    height: '100%',
    width: '100%',
    borderRadius: 15,
  },
  subContainer: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  overlayLabelContainer: {
    width: '100%',
    height: '100%',
    borderRadius: 15,
  },
});

For more examples check out the example folder 📂

Types 🏷️

type SwiperCardRefType =
  | {
      swipeRight: () => void;
      swipeLeft: () => void;
      swipeBack: () => void;
      swipeTop: () => void;
    }
  | undefined;

type SwiperOptions<T> = {
  /*
   * Card data and render function
   */
  data: T[];
  renderCard: (item: T, index: number) => JSX.Element;
  cardStyle?: StyleProp<ViewStyle>;
  /*
   * Children components
   */
  onSwipeLeft?: (cardIndex: number) => void;
  onSwipeRight?: (cardIndex: number) => void;
  onSwipeTop?: (cardIndex: number) => void;
  onSwipedAll?: () => void;
  onSwipeStart?: () => void;
  onSwipeEnd?: () => void;
  onSwipeActive?: () => void;
  /*
   * Swipe methods
   */
  disableRightSwipe?: boolean;
  disableLeftSwipe?: boolean;
  disableTopSwipe?: boolean;
  /*
   * Rotation Animation Props
   */
  translateXRange?: number[];
  translateYRange?: number[];
  rotateInputRange?: number[];
  rotateOutputRange?: number[];
  /*
   * Overlay Labels Animation Props
   */
  inputOverlayLabelRightOpacityRange?: number[];
  outputOverlayLabelRightOpacityRange?: number[];
  inputOverlayLabelLeftOpacityRange?: number[];
  outputOverlayLabelLeftOpacityRange?: number[];
  inputOverlayLabelTopOpacityRange?: number[];
  outputOverlayLabelTopOpacityRange?: number[];
  OverlayLabelRight?: () => JSX.Element;
  OverlayLabelLeft?: () => JSX.Element;
  OverlayLabelTop?: () => JSX.Element;
};

Contributing 🔖

See the contributing guide to learn how to contribute to the repository and the development workflow.

License 📰

MIT

rn-swiper-list's People

Contributors

joehoel avatar joshrozin avatar kdong007 avatar skipperlla avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

rn-swiper-list's Issues

Using SwiperCard on a FlatList

Hello !

Thank you for the major upgrade !
I saw that using a single card on a flatList is now possible on your readme but I don't see any example.

I think we must use SwiperCard and use it as a item on the flatList but this component can't be imported on my project, it seems that this element is missing an export.

Can you tell me what I need to do ?
Thanks !

Swiper is too laggy.

When I add FlatList, pinar, any images carousel with direction horizontal inside renderCard then swiper is too laggy.
I'm rendering 20 cards and its sometimes stuck in 10th card in SUMSUNG S23 Ultra

Swipe amount

Please, can you add the amount of the swipe to the onSwipeActive event? I would like to add an animation that is played based on the swipe but at the moment I have not found any way to derive this value.

Thankyou 🙏🏻

Type issue: 'Swiper' cannot be used as a JSX component.

Hi!

First of all, thank you so much for your library and amazing work behind it!

I have a question about typescript issue I get after upgrading to v2.0.2 (might be unrelated, but worth to mention). I migrated the code to Swiper and now type check fails with this error:

error TS2786: 'Swiper' cannot be used as a JSX component.
  Its return type 'ReactNode' is not a valid JSX element.
    Type 'undefined' is not assignable to type 'Element | null'.

140         <Swiper
             ~~~~~~

As I understand, since Swiper's type might be undefined, it's incompatible with expected by JSX 'Element | null' type

const Swiper: <T>(props: SwiperOptions<T> & React.RefAttributes<{
    swipeRight: () => void;
    swipeLeft: () => void;
    swipeBack: () => void;
    swipeTop: () => void;
} | undefined>) => React.ReactNode
import Swiper

My guess is changing SwiperCardRefType to .. | null instead of .. | undefined should address that, but I'm not sure.

Would love to hear your thoughts and can help bringing the fix!

Fetching data when swipe deck is empty

Hello. I tried following the example in the repo . I used useState to store data and changed onSwipedRight and onSwipedLeft callbacks to update the state. The view gets re-rendered but it's empty
Here's my code

export default function App() {
  const [data,setData] = React.useState( [
    'https://images.unsplash.com/photo-1681896616404-6568bf13b022?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1335&q=80',
    'https://images.unsplash.com/photo-1681871197336-0250ed2fe23d?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1287&q=80',
  ])

  const loadData = (index) => {
    if(index == 0) {
      console.log("loading data")
      setData([
      'https://images.unsplash.com/photo-1681238091934-10fbb34b497a?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1282&q=80',
      ])
    }
  }
  return (
    <View style={{ flexDirection: "column", display: "flex", flex: 1, backgroundColor: "#14110f" }} >
      <GestureHandlerRootView style={styles.wrapper} >
        {data.map((item, index) => {
          console.log(item)
          return (
            <View
              style={styles.cardContainer}
              pointerEvents="box-none"
              key={index}
            >
              <TinderCard
                cardWidth={338}
                cardHeight={600}
                cardStyle={styles.card}
                onSwipedRight={() => {
                  loadData(index)
                }}
                onSwipedLeft={() => {
                  loadData(index)
                }}
              >
                <ImageBackground source={{ uri: item }} style={styles.image} >
                  <View style={{ position: 'absolute',height:"100%",width:"100%",  justifyContent:'flex-end', alignItems: 'center',paddingBottom:20, backgroundColor: 'rgba(0,0,0, 0.40)' }}>
                    <Text>Title goes here</Text>
                  </View>
                </ImageBackground>
              </TinderCard>
            </View>
          );
        })}
      </GestureHandlerRootView>

    </View>


  );
}

I have ommited the imports and styling as that's unrelated to the issue.

Demo indication of swipe gesture

Feature request

Our users don't instinctively understand that the the cards can be swiped upon app launch. Could we implement a sort of "demo" card swipe animation for the component, indicating the swipe gesture?

Memory leak

I have just updated to 2.0.2 as I have realized that there is a memory leak in the previous version ("rn-tinder-card": "1.6.0",), even if manually calling gc.

However, it seems to me 2.0.2 also does have this problem. When looking at the demo mp4 in the README: it starts from 209MB and after 30 sec it has 214MB. The problematic thing is that using swipeBack and any swipe again clearly leads to more and more memory too.

@Skipperlla
Any ideas where to start? I'd like to help with this as I need functionality provided by this lib asap. :)

Property is not configurable

Hi!
I have a problem with passing the updated data array when re-entering the screen where I use your list. Being on screen 1, I click on the second photo, move to screen 2 where I use your list, click the heart to change the attribute in the data array on screen 1 using Redux, go back to screen 1, and see the heart on the second photo. When I want to view this photo again, I click on it to go back to screen 2, and a Reanimated library error pops up. I suspect that the list keeps certain values that do not reset. Sometimes an error with the description appears - [Reanimated] Tried to modify key ${key} of an object which has been already passed to a worklet. See
https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#tried-to-modify-key-of-an-object-which-has-been-converted-to-a-shareable
for more details.[

Simulator.Screen.Recording.-.iPhone.15.Pro.-.2024-06-13.at.20.39.04.mp4

]

System:
OS: macOS 14.5
CPU: (11) arm64 Apple M3 Pro
Memory: 87.22 MB / 18.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 22.2.0
path: /opt/homebrew/bin/node
Yarn:
version: 1.22.22
path: /opt/homebrew/bin/yarn
npm:
version: 10.7.0
path: /opt/homebrew/bin/npm
Watchman:
version: 2024.05.06.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.15.2
path: /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 23.5
- iOS 17.5
- macOS 14.5
- tvOS 17.5
- visionOS 1.2
- watchOS 10.5
Android SDK: Not Found
IDEs:
Android Studio: 2023.3 AI-233.14808.21.2331.11709847
Xcode:
version: 15.4/15F31d
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.11
path: /usr/bin/javac
Ruby:
version: 2.6.10
path: /usr/bin/ruby
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 18.2.0
wanted: 18.2.0
react-native:
installed: 0.74.1
wanted: 0.74.1
react-native-macos: Not Found
npmGlobalPackages:
"react-native": Not Found
Android:
hermesEnabled: true
newArchEnabled: false
iOS:
hermesEnabled: true
newArchEnabled: false

Apply Opacity on behind Image.

I want to apply Opacity like when we are swipe left/right the card then previous card should be animate with opacity.

Capturing events from children

Hi there!

I am wondering how capturing events of children currently possibly or how to improve. Maybe possible with these steps below, but not otherwise?

  1. exposing a ref to the used PanGestureHandler (to use that ref in children's simultaneousHandlers)
  2. adding a new prop to set simultaneousHandlers of the used PanGestureHandler

@Skipperlla WDYT?

Loop back to beginning of items

Im trying to identify a way to cycle through all the items and then to cycle back to the beginning. Im happy to make the change and PR it in but cant quite work out where to start with it

The card item still appears after swiping down

Thanks for awesome library.
I just ran your example and run it on web npx expo start --web.
However, the card item still appears after swiping down, and it makes the parent height change.

Screen.Recording.2023-11-04.at.7.55.04.PM.mov

Add which side is gesture going

Hey, can You add prop in this functions onSwipeStart?: () => void; onSwipeEnd?: () => void; onSwipeActive?: () => void; which side gesture is going?
Something like this? onSwipeStart?: (cardIndex, 'left' | 'right' | 'top', percentage) => void; onSwipeEnd?: (cardIndex, 'left' | 'right' | 'top', percentage) => void; onSwipeActive?: (cardIndex, 'left' | 'right' | 'top', percentage) => void;

Swipe percentage would be cool too, swiping card by half to side and callback func receive 50% etc. :D

expose userConfig for useImperativeHandle methods

It would be really nice if we could configure this (current behaviour is too fast/too snappy I think):

const userConfig = {
  damping: 15,
  stiffness: 120,
  mass: 0.5,
  overshootClamping: false,
  restDisplacementThreshold: 0.001,
  restSpeedThreshold: 0.001,
};

On Android the card is stuck, can not swipe

i tested on ios, it works fine on ios simulator
But on android the card doesn't move when i touch and drag it, the card only moves when i press the button(like the heart button)
Do you know what the problem is?

2024-08-14-15-13-58.mp4

Question: v1.7 with Reanimated 3

Hi there @Skipperlla !

Just wondering if you've encountered the following problem while you were migrating to v2: from Reanimated 2.14 to Reanimated 3. My issue is that after updating to Reanimated 3, if I use your lib on 1.x there is a little bit of flickering / lagging (with 10-20% chance) when I start to drag a card. The whole card jumps from the current dragged position back to a couple of millisec before and then back to the recent/valid dragged position, then continous its journey properly...

This is 100% due to the reason I updated to Reanimated 3 (to get rid of useAnimatedStyle which caused memory leak), I made no other changes. I also tried to update react-native-gesture-handler and mimic your recent code from v2, but this flickering / lagging is still there.

Have you run into that issue? I am just wondering how can I solve that without restructuring my codebase to be able to use v2 if that's possible.

Swipe Issue

Swiping is really hard in real device.

I want to swipe left and right when user swipe the card almost 50% or 40%. how that's possible.

Touch Events

Could it be possible to use Touchable or Pressable components inside the renderCard method? I've tried and they seem to not receive the touch events.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.