How to Make Elements Draggable with Animation in Anime.js
Anime.js provides a dedicated createDraggable factory that converts any DOM element into a physics-aware draggable object powered by the internal Animatable class, enabling real-time coordinate updates, spring-based release animations, and seamless synchronization with timelines.
The juliangarnier/anime library extends beyond traditional tweening with a complete drag-and-drop system built on its animation engine. Unlike standalone drag libraries, this implementation uses the same Animatable infrastructure found in src/animatable/animatable.js, allowing you to combine user-driven motion with programmatic effects. By importing createDraggable from dist/modules/index.js, you can make elements draggable while maintaining access to snapping, container bounds, and physics-based release behaviors.
Understanding the Draggable Architecture
Core Components
The draggable system relies on several interconnected classes that bridge user input with the animation engine:
Draggableclass – Defined insrc/draggable/draggable.js(line 219), this handles pointer events, computes velocity, manages container bounds, snapping, spring-based release, autoscroll, and cursor styling.createDraggablefactory – A convenience exporter located at lines 281-287 insrc/draggable/draggable.jsthat instantiates and returns a configuredDraggableobject.Animatableclass – Found insrc/animatable/animatable.js(line 41), this generic wrapper creates per-property animations that theDraggablemanipulates during movement.createSpring– A helper exported fromdist/modules/index.jsthat generates spring easings for release animations.
When instantiated, a Draggable creates an internal Animatable instance stored in this.animate. This wrapper contains individual animations for each draggable property (translateX, translateY, etc.), accessible via calls like this.animate[this.xProp](value, duration, ease).
Event Flow and Update Cycle
The system processes user input through a strict pipeline that maintains synchronization with the animation engine:
- Pointer down –
handleDownrecords the start position, resets velocity, applies a highz-index, and disables page scrolling. - Pointer move –
handleMoveupdates the internal pointer state and triggersthis.updateTicker, which invokes theupdate()method. update()method – Computes new coordinates, applies container friction, enforces bounds, updates theAnimatableviathis.setXandthis.setY, and calculates velocity/angle.- Pointer up –
handleUpcalculates a release trajectory, optionally applies spring physics (releaseXSpring,releaseYSpring) or an easing function, then animates the target to its final resting point.
This pipeline triggers user-provided callbacks (onGrab, onDrag, onRelease, onUpdate, onSnap) at each phase, allowing real-time reactions to motion.
Integration with the Animation Engine
Because Draggable internally uses an Animatable, you can treat draggable elements like standard animation targets. After initialization, you can programmatically animate properties:
// Programmatically animate the draggable to a new position
draggable.animate[draggable.xProp](finalX, 500, 'outQuad');
Alternatively, use the high-level animate helper to modify other properties while the element remains draggable:
import { animate } from 'animejs';
animate(draggable, {
scale: 1.2,
duration: 300,
easing: 'outBack'
});
The instance also exposes progressX and progressY values normalized to container bounds (0 to 1), enabling binding to timeline animations or other effects.
Creating a Basic Draggable Element
To make an element draggable, import createDraggable and pass a CSS selector or DOM element along with configuration options:
<!DOCTYPE html>
<html>
<head>
<style>
.box {
width: 120px;
height: 120px;
background: #e91e63;
border-radius: 8px;
cursor: grab;
}
</style>
<script type="module">
import { createDraggable } from '../../dist/modules/index.js';
createDraggable('#box', {
container: document.body,
snap: 20,
cursor: { onHover: 'grab', onGrab: 'grabbing' },
onDrag: d => console.log('velocity:', d.velocity.toFixed(2))
});
</script>
</head>
<body>
<div id="box" class="box"></div>
</body>
</html>
Key implementation details:
createDraggableautomatically instantiates the internalAnimatablewrapper.- The
snapoption forces the target to land on multiples of 20 pixels. - Cursor handling is built-in via the
cursorconfiguration object.
Implementing Advanced Drag Behaviors
Adding Spring Physics to Release
For a natural throwing effect, configure the releaseEase property with a spring generated by createSpring:
import { createDraggable, createSpring } from 'animejs';
const springEase = createSpring({
mass: 1,
stiffness: 300,
damping: 20
});
createDraggable('#ball', {
container: '#stage',
releaseEase: springEase,
maxVelocity: 80,
onRelease: d => console.log('settled at', d.x, d.y)
});
When handleUp fires, the Draggable applies this spring to smoothly animate the element back into bounds or to its final resting point, with velocity capped by maxVelocity.
Synchronizing Drag Progress with Timelines
You can bind the draggable's normalized position to timeline progress for scrubbing effects:
import { createDraggable, createTimeline } from 'animejs';
const tl = createTimeline({ autoplay: false })
.add('#bg', { opacity: [0, 1] }, 0)
.add('#title', { translateY: [-50, 0] }, 0);
const drag = createDraggable('#thumb', {
container: '#slider',
y: false,
snap: 10,
onUpdate: d => { tl.progress = d.progressX; }
});
As the user drags horizontally across the container, progressX (ranging from 0 to 1) drives the timeline's progress, synchronizing the background fade and title slide with the thumb position.
Summary
- Use
createDraggablefromsrc/draggable/draggable.jsto instantiate draggable elements with a single function call. - Internal
Animatableintegration allows theDraggableclass to reuse the standard animation engine for coordinate updates viathis.setXandthis.setY. - Event-driven architecture processes input through
handleDown,handleMove,update(), andhandleUp, calculating velocity and enforcing bounds in real time. - Spring-based release physics can be applied using
createSpringand assigned to thereleaseEaseparameter for elastic throw effects. - Timeline synchronization is possible by mapping
progressXorprogressY(normalized 0-1 values) to animation timelines or other properties.
Frequently Asked Questions
How do I constrain draggable movement to a specific container?
Pass a DOM element or CSS selector to the container property when calling createDraggable. The system automatically calculates bounds relative to this container and applies friction when the element approaches edges. If the element is thrown outside bounds, it springs back on release.
Can I disable movement on one axis only?
Set the x or y property to false in the configuration object. For example, y: false restricts movement to horizontal dragging only, while still allowing the vertical position to be animated programmatically through the internal Animatable.
How do I implement grid snapping?
Provide a number to the snap parameter, which specifies the pixel grid size. The Draggable instance automatically rounds coordinates to the nearest multiple of this value during the update() cycle, creating a stepped movement effect without manual calculation.
What callbacks are available for reacting to drag events?
The system exposes onGrab (pointer down), onDrag (continuous movement), onRelease (pointer up), onUpdate (every frame), and onSnap (when snapping occurs). Each receives the draggable instance as an argument, providing access to x, y, velocity, and progressX/Y properties.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →