@use-gesture Documentation

repository·main·Indexed 27 days ago

https://github.com/pmndrs/use-gesture

A library for binding rich mouse and touch gestures to components or views, supporting complex interactions like dragging, pinching, and scrolling. It provides specialized hooks for React via @use-gesture/react (including useDrag, usePinch, and useWheel) and gesture classes for Vanilla JavaScript via @use-gesture/vanilla. The library includes utilities for handling multiple gestures simultaneously and tools like createUseGesture and createGesture to optimize bundle size through tree shaking.

Tokens
13.9K
Snippets
34
Records
79
Agent score
94%

What's inside @use-gesture

  1. Understand the benefits of @use-gesture

    main
    Use @use-gesture to simplify the configuration of complex gestures like drag and pinch. It provides a high-level API that makes setting up handlers like onDrag as simple as native handlers like onMouseMove, while still allowing you to maintain full control over the gesture logic for custom component behavior.
  2. Access augmented gesture attributes

    main

    Beyond standard browser events, @use-gesture augments gestures with additional kinematic attributes that are not available in native browser events. These include:

    • velocity: The speed of the gesture.
    • distance: The total distance traveled.
    • delta: The change in position since the last event.

    Additionally, the library automatically debounces scroll, wheel, and move events, enabling you to trigger logic specifically when a gesture starts or ends using built-in handlers.

  3. Structure the configuration object for different APIs

    main

    The structure of your configuration object depends on whether you are using gesture-specific hooks, the general useGesture hook, or the Vanilla JavaScript classes (DragGesture or Gesture).

    • Gesture-specific hooks: Pass shared options and gesture-specific options in a single object.
    • useGesture hook: Pass an object containing event handlers (e.g., onDrag, onPinch) as the first argument, and a second object containing shared options and gesture-specific configuration objects (e.g., drag: { ... }).
    • Vanilla Classes:
      • DragGesture: Pass the element, handler, and a single config object containing shared and drag-specific options.
      • Gesture: Pass the element, an object of handlers, and a second object containing shared and gesture-specific configuration objects.
    // when you use a gesture-specific hook
    useDrag((state) => doSomethingWith(state), { ...sharedOptions, ...dragOptions })
    
    // when you use the useGesture hook
    useGesture(
      {
        onDrag: (state) => doSomethingWith(state),
        onPinch: (state) => doSomethingWith(state)
        // ...
      },
      {
        // global options such as `target`
        ...sharedOptions,
        // gesture specific options
        drag: dragOptions,
        wheel: wheelOptions,
        pinch: pinchOptions,
        scroll: scrollOptions,
        hover: hoverOptions
      }
    )
    
    // when you use a gesture-specific class
    new DragGesture(
      element,
      state => doSomethingWith(state),
      { ...sharedOptions, ...dragOptions }
    )
    
    // when you use the Gesture class
    new Gesture(element, {
      onDrag: state => doSomethingWith(state),
      onPinch: state => doSomethingWith(state),
      // ...
      {
        // global options such as `target`
        ...sharedOptions,
        // gesture specific options
        drag: dragOptions,
        wheel: wheelOptions,
        pinch: pinchOptions,
        scroll: scrollOptions,
        wheel: wheelOptions,
        hover: hoverOptions,
      }
    })
  4. Configure pinch gestures for Safari and Zooming

    main

    The pinch gesture requires specific handling for certain environments:

    1. Safari/Macbook Trackpads: To prevent the gesture from interfering with Safari's accessibility zoom, you may need to prevent the native gesturestart and gesturechange events:
      document.addEventListener('gesturestart', (e) => e.preventDefault())
      document.addEventListener('gesturechange', (e) => e.preventDefault())
    2. React Implementation: Because React does not support proprietary Webkit GestureEvents, you must attach the gesture using a ref and use the target option.
    3. Control + Wheel Zoom: Devices supporting wheel zoom (Control + Wheel) should also use the target option to avoid interfering with browser accessibility zoom.
  5. Prevent body scrolling using touch-action CSS

    main

    When implementing horizontal gestures (like swiping an item to reveal actions) on mobile, you often want to prevent the page from scrolling vertically while the user is interacting with the element.

    Instead of calling event.preventDefault(), use the CSS touch-action property on the element being dragged.

    • Use touch-action: pan-y to allow only vertical scrolling while the element is being interacted with. This allows the browser to natively handle vertical scrolling while your gesture handler manages horizontal movement.
    • Use touch-action: none if you want to disable all browser-native touch interactions (both vertical and horizontal) on that element.

    Important: When using touch-action: pan-y for horizontal gestures, you should also set the { axis: 'x' } option in your gesture handler to ensure the handler only responds to movement on that specific axis.

    function TouchActionExample() {
      const [springs, api] = useSprings(4, () => ({ x: 0 }))
    
      const bind = useDrag(
        ({ down, movement: [x], args: [index] }) => api.start((i) => i === index && { x: down ? x : 0 }),
        {
          axis: 'x'
        }
      )
    
      return springs.map(({ x }, i) => <animated.div {...bind(i)} style={{ x, touchAction: 'pan-y' }} />)
    }
  6. Use movement and offset for relative positioning

    main

    When moving components (e.g., via CSS transform), use movement and offset instead of absolute xy coordinates.

    • movement: The displacement during the current gesture. It resets to [0,0] at the start of every new gesture.
    • offset: The cumulative sum of all gesture movements. Use this if you want a component to stay where it was left after a gesture ends.

    Example: Using offset to keep a component in its new position

    function OffsetExample() {
      const [{ x, y }, api] = useSpring(() => ({ x: 0, y: 0 }))
      const bind = useDrag(({ offset: [x, y] }) => api.start({ x, y }))
      return <animated.div {...bind()} style={{ x, y }} />
    }
  7. Use useDrag in React

    main

    In React, @use-gesture provides hooks like useDrag that you can attach to components. The hook returns a bind function; when called and spread onto a component (e.g., <div {...bind()} />), it attaches necessary pointer event handlers like onPointerDown, onPointerMove, and onPointerUp.

    Note: @use-gesture provides the gesture data, but it is not responsible for the actual movement/animation. You should combine it with an animation library like react-spring to handle component transforms.

    import { useSpring, animated } from '@react-spring/web'
    import { useDrag } from '@use-gesture/react'
    
    function PullRelease() {
      const [{ x, y }, api] = useSpring(() => ({ x: 0, y: 0 }))
    
      // Set the drag hook and define component movement based on gesture data
      const bind = useDrag(({ down, movement: [mx, my] }) => {
        api.start({ x: down ? mx : 0, y: down ? my : 0, immediate: down })
      })
    
      // Bind it to a component
      return <animated.div {...bind()} style={{ x, y }} />
    }
  8. Include native React event handlers in useGesture

    main

    When using useGesture in React, you can include native React event handlers (like onPointerDown) directly within the configuration object. This ensures that the native handler is executed alongside the gesture logic without overwriting the attributes provided by the bind() function.

    Native handlers receive the shared gesture state, including the original event and any arguments passed to bind.

    function DragAndPointerDown() {
      const [{ x, y }, api] = useSpring(() => ({ x: 0, y: 0 }))
      const bind = useGesture({
        onDrag: ({ down, offset: [x, y] }) => api.start({ x, y }),
        onPointerDown: ({ event, ...sharedState }) => console.log('pointer down', event)
      })
      return <animated.div {...bind()} style={{ x, y }} />
    }