3DJakob / react-tinder-card

A npm react module for making react elements swipeable like in the dating app tinder.
https://www.npmjs.com/package/react-tinder-card
MIT License
364 stars 111 forks source link

onPress event not working on Android #137

Open AlexDiMaria opened 1 year ago

AlexDiMaria commented 1 year ago
import {Button} from "react-native-paper";
import TinderCard from "react-tinder-card";

<TinderCard className="pressable">
    <Button
        onPress={() => {
            console.log("Ciao")
        }}
    >
        <Text>
            Ciao
        </Text>
    </Button>
</TinderCard>

I'm trying to create a button inside a card with this simple snippet code on React Native.

When I do test with an iOS emulator all it's working fine, but on Android the onPress event is not recognize... What i've to do?

Matthew3dg commented 1 year ago
import {Button} from "react-native-paper";
import TinderCard from "react-tinder-card";

<TinderCard className="pressable">
    <Button
        onPress={() => {
            console.log("Ciao")
        }}
    >
        <Text>
            Ciao
        </Text>
    </Button>
</TinderCard>

I'm trying to create a button inside a card with this simple snippet code on React Native.

When I do test with an iOS emulator all it's working fine, but on Android the onPress event is not recognize... What i've to do?

I have the same problem. Have you found a solution?

nihilanthmf commented 1 year ago

Got the same issue, has anybody solved it yet?

nihilanthmf commented 1 year ago

Hey guys I've figured it out! Here is the code, this is the exact same thing as the TinderCard (code is literally stolen form this repo) but I've made a small change. You can now pass a function to the TinderCard component called onPress that is executed when you click on a card

import React from 'react'; import {View, PanResponder, Dimensions} from 'react-native'; import {useSpring, animated} from '@react-spring/native'; const {height, width} = Dimensions.get('window');

const settings = { maxTilt: 25, // in deg rotationPower: 50, swipeThreshold: 1, // need to update this threshold for RN (1.5 seems reasonable...?) };

// physical properties of the spring const physics = { touchResponsive: { friction: 50, tension: 2000, }, animateOut: { friction: 30, tension: 400, }, animateBack: { friction: 10, tension: 200, }, };

let gestureGlobal;

const pythagoras = (x, y) => { return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); };

const animateOut = async (gesture, setSpringTarget) => { const diagonal = pythagoras(height, width); const velocity = pythagoras(gesture.vx, gesture.vy); const finalX = diagonal gesture.vx; const finalY = diagonal gesture.vy; const finalRotation = gesture.vx * 45; const duration = diagonal / velocity;

setSpringTarget({ x: finalX, y: finalY, rot: finalRotation, // set final rotation value based on gesture.vx config: {duration: duration}, });

// for now animate back return await new Promise(resolve => setTimeout(() => { resolve(); }, duration), ); };

const animateBack = setSpringTarget => { // translate back to the initial position setSpringTarget({x: 0, y: 0, rot: 0, config: physics.animateBack}); };

const getSwipeDirection = speed => { if (Math.abs(speed.x) > Math.abs(speed.y)) { return speed.x > 0 ? 'right' : 'left'; } else { return speed.y > 0 ? 'down' : 'up'; } };

// must be created outside of the TinderCard forwardRef const AnimatedView = animated(View);

const TinderCard = React.forwardRef( ( { flickOnSwipe = true, children, onSwipe, onCardLeftScreen, className, onPress, preventSwipe = [], }, ref, ) => { const [{x, y, rot}, setSpringTarget] = useSpring(() => ({ x: 0, y: 0, rot: 0, config: physics.touchResponsive, }));

React.useImperativeHandle(ref, () => ({
  async swipe(dir = 'right') {
    if (onSwipe) onSwipe(dir);
    const power = 1.3;
    const disturbance = (Math.random() - 0.5) / 2;
    if (dir === 'right') {
      await animateOut({vx: power, vy: disturbance}, setSpringTarget);
    } else if (dir === 'left') {
      await animateOut({vx: -power, vy: disturbance}, setSpringTarget);
    } else if (dir === 'up') {
      await animateOut({vx: disturbance, vy: power}, setSpringTarget);
    } else if (dir === 'down') {
      await animateOut({vx: disturbance, vy: -power}, setSpringTarget);
    }
    if (onCardLeftScreen) onCardLeftScreen(dir);
  },
}));

const handleSwipeReleased = React.useCallback(
  async (setSpringTarget, gesture) => {
    // Check if this is a swipe
    if (
      Math.abs(gesture.vx) > settings.swipeThreshold ||
      Math.abs(gesture.vy) > settings.swipeThreshold
    ) {
      const dir = getSwipeDirection({x: gesture.vx, y: gesture.vy});

      if (flickOnSwipe) {
        if (!preventSwipe.includes(dir)) {
          if (onSwipe) onSwipe(dir);

          await animateOut(gesture, setSpringTarget);
          if (onCardLeftScreen) onCardLeftScreen(dir);
          return;
        }
      }
    }
    gestureGlobal = gesture;

    // Card was not flicked away, animate back to start
    animateBack(setSpringTarget);
  },
  [flickOnSwipe, onSwipe, onCardLeftScreen, preventSwipe],
);

const panResponder = React.useMemo(
  () =>
    PanResponder.create({
      // Ask to be the responder:
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,

      onPanResponderGrant: (evt, gestureState) => {
        // The gesture has started.
        // Probably wont need this anymore as postion i relative to swipe!
        setSpringTarget({
          x: gestureState.dx,
          y: gestureState.dy,
          rot: 0,
          config: physics.touchResponsive,
        });
      },
      onPanResponderMove: (evt, gestureState) => {
        // use guestureState.vx / guestureState.vy for velocity calculations
        // translate element
        let rot = ((300 * gestureState.vx) / width) * 15; // Magic number 300 different on different devices? Run on physical device!
        rot = Math.max(Math.min(rot, settings.maxTilt), -settings.maxTilt);
        setSpringTarget({
          x: gestureState.dx,
          y: gestureState.dy,
          rot,
          config: physics.touchResponsive,
        });
      },
      onPanResponderTerminationRequest: (evt, gestureState) => {
        return true;
      },
      onPanResponderRelease: (evt, gestureState) => {
        // The user has released all touches while this view is the
        // responder. This typically means a gesture has succeeded
        // enable
        handleSwipeReleased(setSpringTarget, gestureState);
        if (Math.abs(gestureGlobal.vx) <= 0.005) {
          onPress();
        }
      },
    }),
  [],
);

return (
  <AnimatedView
    {...panResponder.panHandlers}
    style={{
      transform: [
        {translateX: x},
        {translateY: y},
        {rotate: rot.interpolate(rot => `${rot}deg`)},
      ],
    }}>
    {children}
  </AnimatedView>
);

}, );

// module.exports = TinderCard export default TinderCard;