如何识别 React 中的滑动事件?

gru*_*gru 16 html javascript mobile event-handling reactjs

我目前正在开发一个 React 应用程序,我想检测元素div(在移动设备上)上的滑动事件(左、右)。

在没有任何额外库的情况下如何实现这一目标?

gru*_*gru 41

水平滑动(左、右)

此代码检测左右滑动事件,不会对通常的触摸事件产生任何影响。

const [touchStart, setTouchStart] = useState(null)
const [touchEnd, setTouchEnd] = useState(null)

// the required distance between touchStart and touchEnd to be detected as a swipe
const minSwipeDistance = 50 

const onTouchStart = (e) => {
  setTouchEnd(null) // otherwise the swipe is fired even with usual touch events
  setTouchStart(e.targetTouches[0].clientX)
}

const onTouchMove = (e) => setTouchEnd(e.targetTouches[0].clientX)

const onTouchEnd = () => {
  if (!touchStart || !touchEnd) return
  const distance = touchStart - touchEnd
  const isLeftSwipe = distance > minSwipeDistance
  const isRightSwipe = distance < -minSwipeDistance
  if (isLeftSwipe || isRightSwipe) console.log('swipe', isLeftSwipe ? 'left' : 'right')
  // add your conditional logic here
}
Run Code Online (Sandbox Code Playgroud)
<div onTouchStart={onTouchStart} onTouchMove={onTouchMove} onTouchEnd={onTouchEnd}/>
Run Code Online (Sandbox Code Playgroud)

垂直滑动(向上、向下)

如果您还需要检测垂直滑动(向上和向下),您可以以类似的方式使用e.targetTouches[0].clientY(请参阅文档)。

  • 很好的解决方案!为了获得更好的性能,您可以使用 refs 而不是 state。在这种情况下,使用 refs 可以避免不必要的组件重新渲染。 (10认同)

E L*_*E L 10

@gru 提供的优秀解决方案。我只是将其封装到自定义挂钩中,以便更简单地在不同组件之间集成。

useSwipe.tsx

import {TouchEvent, useState} from "react";

interface SwipeInput {
    onSwipedLeft: () => void
    onSwipedRight: () => void
}

interface SwipeOutput {
    onTouchStart: (e: TouchEvent) => void
    onTouchMove: (e: TouchEvent) => void
    onTouchEnd: () => void
}

export default (input: SwipeInput): SwipeOutput => {
    const [touchStart, setTouchStart] = useState(0);
    const [touchEnd, setTouchEnd] = useState(0);

    const minSwipeDistance = 50;

    const onTouchStart = (e: TouchEvent) => {
        setTouchEnd(0); // otherwise the swipe is fired even with usual touch events
        setTouchStart(e.targetTouches[0].clientX);
    }

    const onTouchMove = (e: TouchEvent) => setTouchEnd(e.targetTouches[0].clientX);

    const onTouchEnd = () => {
        if (!touchStart || !touchEnd) return;
        const distance = touchStart - touchEnd;
        const isLeftSwipe = distance > minSwipeDistance;
        const isRightSwipe = distance < -minSwipeDistance;
        if (isLeftSwipe) {
            input.onSwipedLeft();
        }
        if (isRightSwipe) {
            input.onSwipedRight();
        }
    }

    return {
        onTouchStart,
        onTouchMove,
        onTouchEnd
    }
}
Run Code Online (Sandbox Code Playgroud)

可以像这样集成到不同的组件中

  1. import useSwipe from "whatever-path/useSwipe";
  2. const swipeHandlers = useSwipe({ onSwipedLeft: () => console.log('left'), onSwipedRight: () => console.log('right') });
  3. <div {...swipeHandlers}>some swipeable div (or whatever html tag)</div>


rem*_*mco 5

@gru 的代码工作得很好,唯一的缺点是,当用户尝试向下滚动并稍微向一侧移动时,它还会检测到滑动。

我的更改确保仅当水平移动大于垂直移动时才检测到滑动:

const distanceX = touchStartX - touchEndX
const distanceY = touchStartY - touchEndY
const isLeftSwipe = distanceX > minSwipeDistance
const isRightSwipe = distanceX < -minSwipeDistance

if (isRightSwipe && Math.abs(distanceX) > distanceY) {
  // add your conditional logic here
} 
if (isLeftSwipe && distanceX > distanceY) {
  // add your conditional logic here
}
Run Code Online (Sandbox Code Playgroud)