How to Implement Drag and Drop in Pixi.js
Implementing drag-and-drop functionality in Pixi.js involves enabling interactivity on a display object, listening to pointer events, and updating the object’s coordinates based on pointer movement. This article provides a step-by-step guide and a complete code example to create a smooth, responsive drag-and-drop interaction that handles multi-touch inputs and prevents the object from snapping to the cursor’s center point.
Step 1: Enable Interactivity on the Object
By default, Pixi.js display objects (like Sprites or Graphics) do not
respond to user input. To make an object draggable, you must set its
eventMode property to 'static' (or
'dynamic' if it moves independently) and change the cursor
to a pointer for better user feedback.
const sprite = PIXI.Sprite.from('path/to/image.png');
// Enable interaction
sprite.eventMode = 'static';
sprite.cursor = 'pointer';Step 2: Handle Pointer Events
To create a natural dragging experience, you need to listen to three
main pointer events: * pointerdown: Triggered when the user
clicks or touches the object. * pointermove: Triggered when
the pointer moves. * pointerup /
pointerupoutside: Triggered when the user releases the
click/touch, even if the pointer is no longer directly over the
object.
sprite.on('pointerdown', onDragStart);Step 3: Write the Drag Event Handlers
The event handlers manage the dragging state. To prevent the object’s center or anchor point from “snapping” to the mouse cursor when clicked, calculate the offset between the pointer’s click position and the object’s origin.
function onDragStart(event) {
// Store a reference to the event data to track this specific pointer (supports multi-touch)
this.data = event.data;
this.alpha = 0.5; // Visually indicate dragging
// Get the pointer's position relative to the object's parent container
const newPosition = this.data.getLocalPosition(this.parent);
// Calculate and store the offset
this.offsetX = newPosition.x - this.x;
this.offsetY = newPosition.y - this.y;
// Listen for movement and release on the parent container for smoother tracking
this.parent.on('pointermove', onDragMove, this);
this.parent.once('pointerup', onDragEnd, this);
this.parent.once('pointerupoutside', onDragEnd, this);
}
function onDragMove(event) {
if (this.data) {
// Retrieve the updated pointer position
const newPosition = this.data.getLocalPosition(this.parent);
// Update the object's coordinates while maintaining the initial click offset
this.x = newPosition.x - this.offsetX;
this.y = newPosition.y - this.offsetY;
}
}
function onDragEnd() {
if (this.data) {
this.alpha = 1; // Restore original opacity
// Remove tracking listeners from the parent container
this.parent.off('pointermove', onDragMove, this);
this.parent.off('pointerup', onDragEnd, this);
this.parent.off('pointerupoutside', onDragEnd, this);
this.data = null; // Clear the pointer reference
}
}Why This Method is Smooth and Robust
- Offset Calculations: Subtracting
this.offsetXandthis.offsetYensures the object moves smoothly from the exact pixel where the user grabbed it, rather than snapping its anchor point directly to the mouse cursor. - Parent-Level Event Binding: Binding the
pointermoveandpointerupevents tothis.parent(or the stage) instead of the sprite itself ensures that even if the user moves the mouse very quickly and temporarily leaves the bounds of the sprite, the drag action is not interrupted. - Multi-Touch Compatibility: Storing
event.dataallows Pixi.js to track individual touch points independently, enabling multiple objects to be dragged simultaneously on touch screens.