Lynx/Modules/Gestures/Draggable
@sigx/lynx-gestures · Stable · Component library

Draggable#

A draggable container built on Gesture.Pan(). It drives the element's transform on the main thread via two useAnimatedStyle bindings, with axis lock, bounds clamping, snap-back, external SharedValue position exposure, and automatic ScrollView coordination plus edge-scroll.

Import#

TSX
import { Draggable } from '@sigx/lynx-gestures';

Basic usage#

Put the element you want to move in the default slot. Draggable tracks the pan on the main (MT/Lepus) thread, so dragging stays locked to the frame rate with no per-frame thread crossings. The dragStart and dragEnd callbacks are dispatched to the background thread.

TSX
import { Draggable } from '@sigx/lynx-gestures';

function DragBox() {
    return (
        <Draggable
            onDragStart={(e) => console.log('start', e.x, e.y)}
            onDragEnd={(e) => console.log('end', e.x, e.y, e.vx, e.vy)}
        >
            <view style={{ width: '80px', height: '80px', backgroundColor: 'tomato' }} />
        </Draggable>
    );
}

The dragEnd payload carries the terminal position (x, y) and velocity (vx, vy, page px per ms).

Axis lock, bounds, and snap-back#

axis locks movement to 'x', 'y' or 'both' (the default). minX / maxX / minY / maxY clamp the translation, threshold sets the minimum pan distance before the drag engages, and snapBack returns the element to its origin on release.

TSX
import { Draggable } from '@sigx/lynx-gestures';

function Slider() {
    return (
        <Draggable axis="x" threshold={4} minX={-120} maxX={120} snapBack>
            <view style={{ width: '64px', height: '64px', backgroundColor: 'royalblue' }} />
        </Draggable>
    );
}

Reading the live position#

Pass external SharedValues to translateX / translateY to observe or reuse the live position — the worklet writes them every frame, and you can read them reactively on the background thread.

TSX
import { Draggable } from '@sigx/lynx-gestures';
import { useSharedValue } from '@sigx/lynx';

function TrackedBox() {
    const x = useSharedValue(0);
    const y = useSharedValue(0);

    return (
        <view>
            <Draggable axis="both" translateX={x} translateY={y}>
                <view style={{ width: '80px', height: '80px', backgroundColor: 'tomato' }} />
            </Draggable>
            <text>Live x: {x.value.toFixed(0)}px</text>
        </view>
    );
}

Edge-scroll inside a ScrollView#

For drag-to-reorder inside a list, wrap the list in ScrollView and set edgeScroll — the parent scroll auto-scrolls when the drag nears its viewport edge. Descendant Draggables auto-yield the native scroll during a gesture, so no extra wiring is required.

TSX
import { Draggable } from '@sigx/lynx-gestures';
import { useSharedValue } from '@sigx/lynx';

function ReorderRow() {
    const y = useSharedValue(0);

    return (
        <Draggable edgeScroll={{ threshold: 50, maxSpeed: 800 }} translateY={y}>
            <view>{/* row content */}</view>
        </Draggable>
    );
}

edgeScroll accepts true or a config object; it defaults to a threshold of 50pt and maxSpeed of 800pt/sec.

Props#

PropTypeDescription
axis'x' | 'y' | 'both'Locks movement to one axis or allows both. Default 'both'.
thresholdnumberMinimum pan distance before the drag engages. Default 0.
snapBackbooleanReturns the element to its origin on release. Default false.
minXnumberLower bound for the horizontal translation.
maxXnumberUpper bound for the horizontal translation.
minYnumberLower bound for the vertical translation.
maxYnumberUpper bound for the vertical translation.
translateXSharedValue<number>Optional external shared value the worklet writes the live X each frame.
translateYSharedValue<number>Optional external shared value the worklet writes the live Y each frame.
edgeScrollboolean | { threshold?: number; maxSpeed?: number }Auto-scrolls a parent ScrollView when the drag nears its edge. Defaults: threshold 50pt, maxSpeed 800pt/sec.
classstringExtra CSS classes.
styleRecord<string, string | number>Inline style. Keep it structurally stable — a background SET_STYLE can clobber the main-thread transform writes.

Slots#

SlotDescription
defaultThe element to drag.

Events#

EventTypeDescription
onDragStart(detail: { x: number; y: number }) => voidFired when the drag begins, with the starting position.
onDragEnd(detail: DragEndDetail) => voidFired on release. DragEndDetail is { x, y, vx, vy } — terminal position and velocity (page px per ms).

See also#

  • API reference — every export, typed, including DragEndDetail, EdgeScrollConfig, ScrollView, and useScrollContext.
  • Swipeable — swipe-to-reveal rows built on the same pan engine.