Skip to content

DragListener

DragListener (from scenerystack/scenery) is a PressListener subclass specialized for dragging: it tracks a pointer from press through release, converts the pointer's position into local/parent/model coordinate frames, and (optionally) writes the result straight into a positionProperty. As PressListener and FireListener documents, PressListener is the shared base for both — FireListener adds a fire() callback for clicks, while DragListener adds coordinate tracking and repositioning for drags. If you also need keyboard support on the same draggable object, reach for RichDragListener instead, which composes a DragListener with a KeyboardDragListener rather than requiring you to wire up both yourself.

ts
import { Node, DragListener } from 'scenerystack/scenery';
import { Vector2 } from 'scenerystack/dot';
import { Property } from 'scenerystack/axon';

const positionProperty = new Property( new Vector2( 0, 0 ) );

const body = new Node( { cursor: 'pointer' } );
body.translation = positionProperty.value;

body.addInputListener( new DragListener( {
  positionProperty: positionProperty,
  transform: modelViewTransform, // maps model <-> view (parent) coordinates
  dragBoundsProperty: model.dragBoundsProperty,
  start: () => body.moveToFront(),
  end: () => { /* drag finished */ }
} ) );

positionProperty.link( position => {
  body.translation = modelViewTransform.modelToViewPosition( position );
} );

This is the typical PhET pattern: give DragListener a positionProperty and a transform, and it keeps the model position in sync with the pointer without you writing any coordinate math. If you'd rather read the drag out manually, omit positionProperty and use the drag callback together with listener.modelDelta/listener.modelPoint.

Options

DragListenerOptions combines everything PressListener accepts (targetNode, attach, mouseButton, pressCursor, canStartPress, collapseDragEvents, …) with:

OptionDefaultEffect
positionPropertynullA Property-like object (needs only a settable .value) kept in sync with the drag, in the model coordinate frame
transformnullA Transform3 or TReadOnlyProperty<Transform3> mapping the parent (view) frame to the model frame
dragBoundsPropertynullConstrains the model position to a Bounds2 (via closestPointTo); combine with mapPosition for custom constraints
mapPositionnull(point: Vector2) => Vector2 — custom mapping from desired to allowed model position, applied before dragBoundsProperty
translateNodefalseIf true, directly sets the drag target's translation during the drag, instead of (or in addition to) positionProperty
start / drag / endno-opsPreferred over overriding press/release — these fire after the listener has already updated its internal state for the event
allowTouchSnagtrueWhether an unattached touch that moves across the target will "snag" and start a press — helps with small draggable targets
applyOffsettrueWhether the initial offset between the pointer and the target's local origin is preserved throughout the drag
useParentOffsetfalseCompute the offset in the parent coordinate frame from positionProperty instead of the target Node's actual transform — for cases where no single Node's transform represents the dragged thing
trackAncestorsfalseIf true, an internal TransformTracker watches ancestor transforms and repositions automatically when they change
offsetPositionnull(point, listener) => Vector2 added to the parent point before computation — useful to offset touch drags away from being directly under the finger
canClickfalseDragListener normally can't be "clicked" (a single tap would pick up and immediately drop); set true to allow accessible single-activation in addition to dragging

Reading the drag state

MemberMeaning
isPressedProperty / isUserControlledPropertyWhether a drag is in progress (isUserControlledProperty is just a more readable alias for the same Property)
globalPoint / parentPoint / localPoint / modelPointDefensive-copy getters for the current drag point in each coordinate frame
modelDeltaThe change in modelPoint since the previous reposition
dragBoundsThe current value of dragBoundsProperty
reposition( globalPoint )Recomputes all the coordinate-frame points and applies positionProperty/translateNode; called automatically on pointer move
interrupt()Cancels an in-progress drag without it looking like a normal release
dispose()Releases the listener's Property/Action resources

DragListener is not itself keyboard-accessible

DragListener only responds to pointer input (mouse/touch/pen). If a draggable object also needs to work with a keyboard, don't hand-wire a separate KeyboardDragListener alongside it — use RichDragListener, which composes both and keeps their shared options (positionProperty, transform, dragBoundsProperty, start/drag/end) in sync automatically.

tandem defaults to required, and drag actions are read-only for PhET-iO

Like PressListener/FireListener, DragListener defaults tandem to Tandem.REQUIRED. Its internal dragAction is also phetioReadOnly: true by default — PhET-iO can observe drag events in the data stream, but can't trigger them, since simulating a drag requires more state than a single data-stream call can carry.