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:

  • Draggable class – Defined in src/draggable/draggable.js (line 219), this handles pointer events, computes velocity, manages container bounds, snapping, spring-based release, autoscroll, and cursor styling.
  • createDraggable factory – A convenience exporter located at lines 281-287 in src/draggable/draggable.js that instantiates and returns a configured Draggable object.
  • Animatable class – Found in src/animatable/animatable.js (line 41), this generic wrapper creates per-property animations that the Draggable manipulates during movement.
  • createSpring – A helper exported from dist/modules/index.js that 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:

  1. Pointer down – handleDown records the start position, resets velocity, applies a high z-index, and disables page scrolling.
  2. Pointer move – handleMove updates the internal pointer state and triggers this.updateTicker, which invokes the update() method.
  3. update() method – Computes new coordinates, applies container friction, enforces bounds, updates the Animatable via this.setX and this.setY, and calculates velocity/angle.
  4. Pointer up – handleUp calculates 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:

  • createDraggable automatically instantiates the internal Animatable wrapper.
  • The snap option forces the target to land on multiples of 20 pixels.
  • Cursor handling is built-in via the cursor configuration 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 createDraggable from src/draggable/draggable.js to instantiate draggable elements with a single function call.
  • Internal Animatable integration allows the Draggable class to reuse the standard animation engine for coordinate updates via this.setX and this.setY.
  • Event-driven architecture processes input through handleDown, handleMove, update(), and handleUp, calculating velocity and enforcing bounds in real time.
  • Spring-based release physics can be applied using createSpring and assigned to the releaseEase parameter for elastic throw effects.
  • Timeline synchronization is possible by mapping progressX or progressY (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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →