NodeJS 在React Native中检测向左滑动

k5ifujac  于 2023-05-06  发布在  Node.js
关注(0)|答案(9)|浏览(185)

如何在React Native中检测整个屏幕上的左滑动?
是否有必要使用PanResponder或可以做得更容易一点?

lh80um4z

lh80um4z1#

我发现react-native-swipe-gestures并不稳定(swipes在android上随机工作),react-native-gesture-handler过于复杂(仅仅添加到项目中就花费了太多精力)。
基于Kuza Grave的答案的简化解决方案,谁的解决方案完美且非常简单:

<View
      onTouchStart={e=> this.touchY = e.nativeEvent.pageY}
      onTouchEnd={e => {
        if (this.touchY - e.nativeEvent.pageY > 20)
          console.log('Swiped up')
      }}
      style={{height: 300, backgroundColor: '#ccc'}}
    />
zlhcx6iw

zlhcx6iw2#

我用scrollviews和touch position做了这个简单的解决方案
它有一个非常干净的实现,没有沉重的组件或外部模块。
您还可以将其用于<View>组件,而不是scrollviews。
首先,我们创建一个hookuseSwipe.tsx

import { Dimensions } from 'react-native';
const windowWidth = Dimensions.get('window').width;

export function useSwipe(onSwipeLeft?: any, onSwipeRight?: any, rangeOffset = 4) {

    let firstTouch = 0
    
    // set user touch start position
    function onTouchStart(e: any) {
        firstTouch = e.nativeEvent.pageX
    }

    // when touch ends check for swipe directions
    function onTouchEnd(e: any){

        // get touch position and screen size
        const positionX = e.nativeEvent.pageX
        const range = windowWidth / rangeOffset

        // check if position is growing positively and has reached specified range
        if(positionX - firstTouch > range){
            onSwipeRight && onSwipeRight()
        }
        // check if position is growing negatively and has reached specified range
        else if(firstTouch - positionX > range){
            onSwipeLeft && onSwipeLeft()
        }
    }

    return {onTouchStart, onTouchEnd};
}

那么在你的组件中在我的例子中,我将用途:exampleComponent.tsx

  • 导入之前的useSwipe钩子。
  • onTouchStartonTouchEnd事件添加到scrollView。

示例组件

import * as React from 'react';
import { ScrollView } from 'react-native';
import { useSwipe } from '../hooks/useSwipe'

export function ExampleComponent(props: any) {
    const { onTouchStart, onTouchEnd } = useSwipe(onSwipeLeft, onSwipeRight, 6)

    function onSwipeLeft(){
        console.log('SWIPE_LEFT')
    }

    function onSwipeRight(){
        console.log('SWIPE_RIGHT')
    }
   
    return (
        <ScrollView onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>
            {props.children}
        </ScrollView>
    );
}

您可以随意使用offsetRange属性来处理精度。
并修改原始代码,使其与普通类组件一起使用,而不是与钩子一起使用。

cnwbcb6i

cnwbcb6i3#

有一个现有的组件react-native-swipe-gestures用于处理上下左右方向的滑动手势,请参见https://github.com/glepur/react-native-swipe-gestures

aiazj4mn

aiazj4mn4#

可以使用react-native-swipe-gesture。您不需要使用npm安装任何第三方模块。将文件下载到您的项目中,并按照给定的步骤操作

hfwmuf9z

hfwmuf9z5#

如果您使用的是托管的Expo项目,这里有一个记录的手势处理程序:https://docs.expo.io/versions/latest/sdk/gesture-handler/
源代码文档可以在这里找到:https://docs.swmansion.com/react-native-gesture-handler/docs/
对于刷卡,我认为你需要使用FlingGestureHandlerhttps://docs.swmansion.com/react-native-gesture-handler/docs/handler-fling

gojuced7

gojuced76#

你可以直接从react-native-gesture-handler link to the docs使用FlingGestureHandler。把你的视野包裹起来。这是你做的。

import { Directions, Gesture, GestureDetector } from 'react-native-gesture-handler'

const MyComponentWithLeftSwipe = () => {
    const flingGestureLeft = Gesture
        .Fling()
        .direction(Directions.LEFT)
        .onEnd(() => console.log("I was swiped!")

    return <GestureDetector gesture={flingGestureLeft}>
        <View>
        ...
        </View>
    </GestureDetector>
}
cyej8jka

cyej8jka7#

我正在使用Kuza Grave提供的解决方案,但在三星Galaxy手机上遇到了一个bug,其中onTouchEnd没有像预期的那样被触发。我最终使用PanResponder创建了另一个实现。
SwipeContainer.tsx

import React, { FC, ReactNode } from "react";
import { View, Animated, PanResponder } from "react-native";

type Props = {
  children: ReactNode;
  onSwipeRight: () => void;
  onSwipeLeft: () => void;
};

const SWIPE_THRESHOLD = 200;

export const SwipeContainer: FC<Props> = ({
  children,
  onSwipeLeft,
  onSwipeRight,
}) => {
  const panResponder = PanResponder.create({
    onStartShouldSetPanResponder: (_evt, _gestureState) => true,
    onPanResponderRelease: (_evt, gestureState) => {
      const { dx } = gestureState;
      if (dx > SWIPE_THRESHOLD) {
        onSwipeRight();
      }
      if (dx < -SWIPE_THRESHOLD) {
        onSwipeLeft();
      }
      // If needed, could add up and down swipes here with `gestureState.dy`
    },
  });

  return (
    <Animated.View {...panResponder.panHandlers}>
      <View>{children}</View>
    </Animated.View>
  );

Example.tsx

import React, { FC } from "react";
import { ChildComponent1, ChildComponent2, SwipeContainer } from "./components";

export const Example: FC = () => {

    const left = () => console.log("left");
    const right = () => console.log("right");

    return (
      <SwipeContainer onSwipeLeft={left} onSwipeRight={right}>
        <ChildComponent1 />
        <ChildComponent2 />
      </SwipeContainer>
    );
};
omjgkv6w

omjgkv6w8#

感谢@Nikhil Gogineni!我把他的代码修改成一个没有componentWillMount的功能组件。
SwipeGesture.tsx

import React, { useEffect } from 'react';
import {
  View,
  Animated,
  PanResponder
} from 'react-native';
/* Credits to: https://github.com/nikhil-gogineni/react-native-swipe-gesture */
const SwipeGesture = (props: any) => {
  const panResponder = React.useRef(
    PanResponder.create({
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onPanResponderRelease: (evt, gestureState) => {
        const x = gestureState.dx;
        const y = gestureState.dy;
        if (Math.abs(x) > Math.abs(y)) {
          if (x >= 0) {
            props.onSwipePerformed('right')
          }
          else {
            props.onSwipePerformed('left')
          }
        }
        else {
          if (y >= 0) {
            props.onSwipePerformed('down')
          }
          else {
            props.onSwipePerformed('up')
          }
        }
      }
    })).current;

  return (
    <Animated.View {...panResponder.panHandlers} style={props.gestureStyle}>
      <View>{props.children}</View>
    </Animated.View>
  )
}

export default SwipeGesture;

而且用法是“同”……感谢Nikhil!

brjng4g3

brjng4g39#

我在丹尼尔的答案之上建立了一个简单的例子,使用钩子,并修复了我在Android上遇到的问题,如果在包含Scroll或具有太多指针事件的子屏幕上使用,最终阻止onTouchEnd事件被触发。

import React, { useCallback, useRef } from 'react'
import { Platform, View } from 'react-native'

const SWIPE_DISTANCE = Platform.OS === 'android' ? 50 : 100 // -> higher = harder to swipe
const MAX_Y_SCROLL_DISTANCE_TO_ALLOW_SWIPE = 100 // -> lower = harder to swipe

// MAX_Y_SCROLL_DISTANCE_TO_ALLOW_SWIPE on most device does not make a big difference, better not filtering too much
// The main factor is the swipe distance

export const MyScreenComponent = () => {
  const touchX = useRef(0)
  const touchY = useRef(0)

  const onSwipeTab = useCallback((direction: 'next' | 'previous') => {
    switch (direction) {
      case 'next':
        // do something
        break
      case 'previous':
      // do something
    }
  }, [])

  const onTouchStart = useCallback((e: any) => {
    const { pageX, pageY } = e.nativeEvent
    touchX.current = pageX
    touchY.current = pageY
  }, [])

  const onTouchEnd = useCallback(
    (e: any) => {
      const { pageX, pageY } = e.nativeEvent
      const diffX = touchX.current - pageX
      const absDiffY = Math.abs(touchY.current - pageY)
      if (absDiffY < MAX_Y_SCROLL_DISTANCE_TO_ALLOW_SWIPE) {
        if (diffX > SWIPE_DISTANCE) {
          onSwipeTab('next')
        } else if (diffX < -SWIPE_DISTANCE) {
          onSwipeTab('previous')
        }
      }
    },
    [onSwipeTab],
  )

  return (
    <View onTouchCancel={onTouchEnd} onTouchEnd={onTouchEnd} onTouchStart={onTouchStart}>
      {/* Your screen */}
    </View>
  )
}

使用像react-native-swipe-gestures这样的库可能是一个很好的选择,但在某些情况下,这已经足够好了,并且避免了额外的第三方依赖。

相关问题