PanResponder

PanResponder is React Native 36,878 's built-in, JavaScript-side gesture API. It sits on the gesture responder system: on each touch, React Native asks the views under the finger, innermost first, whether they want to become the responder (onStartShouldSetPanResponder, or onMoveShouldSetPanResponder once the finger moves); the winner receives onPanResponderGrant, Move and Release, and can lose the touch to another view through onPanResponderTerminate. Every callback gets the native event and a gestureState with x0/y0 (where the grant happened), dx/dy (the distance since), vx/vy (velocity), moveX/moveY and numberActiveTouches. The classic use is dragging with an Animated.ValueXY:

src/interact/DragResponder.tsx: a cover that springs back after a drag (excerpt)TSX
  const pan = useRef(new Animated.ValueXY()).current;
  const responder = useRef(PanResponder.create({
    onStartShouldSetPanResponder: () => true,    // claim the touch on press
    onPanResponderGrant: (_, g) => console.log('grant at', r(g.x0), r(g.y0)),
    onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }],
      { useNativeDriver: false }),                 // gestureState.dx/dy -> pan.x/y
    onPanResponderRelease: (_, g) => {
      console.log(`release dx=${r(g.dx)} dy=${r(g.dy)} vx=${g.vx.toFixed(2)}`,
        `moveX=${r(g.moveX)} touches=${g.numberActiveTouches}`);
      Animated.spring(pan, { toValue: { x: 0, y: 0 }, useNativeDriver: false }).start();
    },
    onPanResponderTerminate: () => console.log('another responder took over'),
  })).current;
// ...spread responder.panHandlers on an Animated.View moved by pan.getTranslateTransform()

One adb shell input swipe, logged through adb logcat -s ReactNativeJS:

Output of 114
 ReactNativeJS: 'grant at', 206, 152
 ReactNativeJS: 'release dx=97 dy=112 vx=0.20', 'moveX=303 touches=0'

Coordinates are in dp, and velocities in dp per millisecond (0.20 here, not 200). Create the responder once, with useRef() or useMemo(); created on every render, it loses track of a gesture in progress. useNativeDriver must be false, because Animated.event() is fed here by a JavaScript callback, not by a native event.