or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

animatable.mdanimation.mddraggable.mdeasings.mdengine.mdindex.mdscope.mdscroll.mdsvg.mdtext.mdtimeline.mdtimer.mdutilities.mdwaapi.md
tile.json

draggable.mddocs/

Draggable

Physics-based draggable system with momentum, constraints, snapping, and smooth interactions for creating intuitive drag interfaces.

Capabilities

createDraggable Function

Create draggable element with physics simulation and boundary constraints.

/**
 * Create draggable element with physics and constraints
 * @param target - Element to make draggable
 * @param parameters - Draggable configuration
 * @returns Draggable instance
 */
function createDraggable(
  target: TargetsParam,
  parameters?: DraggableParams
): Draggable;

Usage Example:

import { createDraggable } from "animejs/draggable";

const draggable = createDraggable(".box", {
  container: ".container",
  onDrag: (self) => {
    console.log(`Position: ${self.x}, ${self.y}`);
  },
});

Draggable Class

Interactive drag functionality with physics simulation, constraints, and snapping.

/**
 * Draggable class for physics-based drag interactions
 */
class Draggable {
  // ===== Element Properties =====

  /** Target element being dragged */
  $target: HTMLElement;

  /** Trigger element for drag interactions (defaults to target) */
  $trigger: HTMLElement;

  /** Container element for boundaries */
  $container: HTMLElement;

  // ===== Position Properties =====

  /**
   * Current X position in pixels
   * @settable Set position directly
   */
  x: number;

  /**
   * Current Y position in pixels
   * @settable Set position directly
   */
  y: number;

  /**
   * X position as progress (0-1) within container bounds
   * 0 = left edge, 1 = right edge
   * @settable Set position by progress
   */
  progressX: number;

  /**
   * Y position as progress (0-1) within container bounds
   * 0 = top edge, 1 = bottom edge
   * @settable Set position by progress
   */
  progressY: number;

  /**
   * X position change since last frame
   */
  deltaX: number;

  /**
   * Y position change since last frame
   */
  deltaY: number;

  /**
   * Current velocity magnitude
   */
  velocity: number;

  /**
   * Drag angle in degrees
   */
  angle: number;

  // ===== State Properties =====

  /**
   * Whether element is currently grabbed (mousedown/touchstart)
   */
  grabbed: boolean;

  /**
   * Whether element is actively being dragged
   */
  dragged: boolean;

  /**
   * Whether element was just released
   */
  released: boolean;

  /**
   * Whether draggable is enabled
   */
  enabled: boolean;

  // ===== Configuration Properties =====

  /**
   * Container padding constraints [top, right, bottom, left]
   * Prevents dragging beyond these offsets from container edges
   */
  containerPadding: [number, number, number, number];

  /**
   * Container edge friction (0-1)
   * Higher values slow down near edges
   */
  containerFriction: number;

  /**
   * X-axis snap points
   * Number: snap to grid increment
   * Array: snap to specific positions
   */
  snapX: number | Array<number>;

  /**
   * Y-axis snap points
   * Number: snap to grid increment
   * Array: snap to specific positions
   */
  snapY: number | Array<number>;

  /**
   * Drag speed multiplier
   * Higher = faster drag response
   */
  dragSpeed: number;

  /**
   * Minimum movement to activate drag (pixels)
   * Prevents accidental drags from clicks
   */
  dragThreshold: number;

  /**
   * Maximum velocity limit
   */
  maxVelocity: number;

  /**
   * Minimum velocity before stopping
   */
  minVelocity: number;

  /**
   * Cursor configuration
   * boolean: enable/disable cursor styles
   * object: custom cursor configuration
   */
  cursor: boolean | DraggableCursorParams;

  // ===== Callbacks =====

  /**
   * Called when element is grabbed (mousedown/touchstart)
   */
  onGrab: Callback<Draggable>;

  /**
   * Called during drag movement
   */
  onDrag: Callback<Draggable>;

  /**
   * Called when element is released
   */
  onRelease: Callback<Draggable>;

  /**
   * Called on position update (drag, momentum, snap)
   */
  onUpdate: Callback<Draggable>;

  /**
   * Called when element settles (velocity reaches zero)
   */
  onSettle: Callback<Draggable>;

  /**
   * Called when element snaps to position
   */
  onSnap: Callback<Draggable>;

  /**
   * Called when container resizes
   */
  onResize: Callback<Draggable>;

  /**
   * Called after resize completes
   */
  onAfterResize: Callback<Draggable>;

  // ===== Methods =====

  /**
   * Set X position programmatically
   * @param x - X position in pixels
   * @param muteUpdateCallback - Skip onUpdate callback
   * @returns Draggable instance for chaining
   */
  setX(x: number, muteUpdateCallback?: boolean): Draggable;

  /**
   * Set Y position programmatically
   * @param y - Y position in pixels
   * @param muteUpdateCallback - Skip onUpdate callback
   * @returns Draggable instance for chaining
   */
  setY(y: number, muteUpdateCallback?: boolean): Draggable;

  /**
   * Refresh bounds and recalculate positions
   * Call after layout changes
   * @returns Draggable instance for chaining
   */
  refresh(): Draggable;

  /**
   * Update draggable state and position
   * @returns Draggable instance for chaining
   */
  update(): Draggable;

  /**
   * Stop current drag and momentum
   * @returns Draggable instance for chaining
   */
  stop(): Draggable;

  /**
   * Scroll container to bring element into view
   * @param duration - Scroll duration in ms
   * @param gap - Gap from viewport edge
   * @param ease - Easing function
   * @returns Draggable instance for chaining
   */
  scrollInView(duration?: number, gap?: number, ease?: EasingParam): Draggable;

  /**
   * Animate element into container view
   * @param duration - Animation duration in ms
   * @param gap - Gap from container edge
   * @param ease - Easing function
   * @returns Draggable instance for chaining
   */
  animateInView(
    duration?: number,
    gap?: number,
    ease?: EasingParam
  ): Draggable;

  /**
   * Reset to initial position
   * @returns Draggable instance for chaining
   */
  reset(): Draggable;

  /**
   * Enable draggable interactions
   * @returns Draggable instance for chaining
   */
  enable(): Draggable;

  /**
   * Disable draggable interactions
   * @returns Draggable instance for chaining
   */
  disable(): Draggable;

  /**
   * Revert and cleanup draggable
   * Removes all event listeners and animations
   * @returns Draggable instance for chaining
   */
  revert(): Draggable;
}

Usage Examples:

import { createDraggable } from "animejs/draggable";

// Basic draggable
const draggable = createDraggable(".box", {
  onDrag: (self) => {
    console.log(`Position: (${self.x}, ${self.y})`);
    console.log(`Progress: (${self.progressX}, ${self.progressY})`);
  },
});

// With container constraints
createDraggable(".card", {
  container: ".container",
  containerPadding: [10, 10, 10, 10], // Keep 10px from edges
  onGrab: (self) => console.log("Grabbed!"),
  onRelease: (self) => console.log("Released!"),
});

// With snapping
createDraggable(".tile", {
  snapX: 100, // Snap to 100px grid on X
  snapY: 100, // Snap to 100px grid on Y
  onSnap: (self) => console.log("Snapped!"),
});

// Custom trigger element
createDraggable(".panel", {
  trigger: ".panel-handle", // Only drag by handle
  container: ".workspace",
});

Container Constraints

Configure boundaries and padding:

createDraggable(".element", {
  container: ".container",

  // Padding from container edges [top, right, bottom, left]
  containerPadding: [20, 20, 20, 20],

  // Edge friction (0-1)
  containerFriction: 0.8, // Slows down near edges
});

Snapping Behavior

Snap to grid or specific positions:

// Grid snapping
createDraggable(".box", {
  snapX: 50, // Snap to 50px grid on X axis
  snapY: 50, // Snap to 50px grid on Y axis
});

// Specific positions
createDraggable(".box", {
  snapX: [0, 100, 200, 300], // Snap to specific X positions
  snapY: [0, 150, 300], // Snap to specific Y positions
  onSnap: (self) => {
    console.log(`Snapped to: ${self.x}, ${self.y}`);
  },
});

// Mixed snapping
createDraggable(".box", {
  snapX: 25, // Grid on X
  snapY: [0, 100, 200, 300, 400], // Specific positions on Y
});

Drag Physics Configuration

Control drag feel and physics:

createDraggable(".element", {
  // Speed multiplier
  dragSpeed: 1.5, // Faster drag

  // Activation threshold
  dragThreshold: 5, // Must move 5px to start drag

  // Velocity limits
  maxVelocity: 50, // Cap maximum velocity
  minVelocity: 0.1, // Stop below this velocity

  // Container friction
  containerFriction: 0.9, // Slow near edges
});

Custom Trigger Element

Drag by handle or specific element:

// Drag entire card by header only
createDraggable(".card", {
  trigger: ".card-header",
});

// Different trigger than target
createDraggable(".modal", {
  trigger: ".modal-titlebar",
  container: "body",
});

Position Control

Programmatically control position:

const draggable = createDraggable(".box");

// Set position directly
draggable.x = 100;
draggable.y = 200;

// Set by progress (0-1)
draggable.progressX = 0.5; // Center horizontally
draggable.progressY = 0.75; // 75% down

// Using methods
draggable.setX(150);
draggable.setY(250);

// Get current position
console.log(draggable.x, draggable.y);
console.log(draggable.progressX, draggable.progressY);

// Get movement delta
console.log(draggable.deltaX, draggable.deltaY);

// Get velocity and angle
console.log(draggable.velocity);
console.log(draggable.angle);

State Management

Check and control draggable state:

const draggable = createDraggable(".element");

// Check state
console.log(draggable.grabbed); // Is grabbed?
console.log(draggable.dragged); // Is dragging?
console.log(draggable.released); // Just released?
console.log(draggable.enabled); // Is enabled?

// Control state
draggable.enable(); // Enable interactions
draggable.disable(); // Disable interactions
draggable.stop(); // Stop current drag
draggable.reset(); // Reset to start position

Cursor Configuration

Customize drag cursor:

// Simple enable/disable
createDraggable(".box", {
  cursor: true, // Default cursor styles
});

// Custom cursor configuration
createDraggable(".box", {
  cursor: {
    grab: "grab",
    grabbing: "grabbing",
  },
});

// Disable cursor changes
createDraggable(".box", {
  cursor: false,
});

Scroll and Animation

Bring element into view:

const draggable = createDraggable(".element", {
  container: ".scroll-container",
});

// Scroll container to show element
draggable.scrollInView(500, 20, "outQuad");
// duration: 500ms
// gap: 20px from edge
// ease: outQuad

// Animate element into bounds
draggable.animateInView(300, 10, "outElastic");

Refresh After Layout Changes

Update bounds after DOM changes:

const draggable = createDraggable(".element");

// After content loads or resizes
window.addEventListener("resize", () => {
  draggable.refresh();
});

// After dynamic content
loadMoreContent().then(() => {
  draggable.refresh();
});

Callback Lifecycle

Complete drag interaction lifecycle:

createDraggable(".element", {
  onGrab: (self) => {
    console.log("User grabbed element");
    console.log(`Start position: ${self.x}, ${self.y}`);
  },

  onDrag: (self) => {
    console.log("Dragging...");
    console.log(`Current: ${self.x}, ${self.y}`);
    console.log(`Delta: ${self.deltaX}, ${self.deltaY}`);
    console.log(`Velocity: ${self.velocity}`);
  },

  onRelease: (self) => {
    console.log("Released");
    console.log(`Final velocity: ${self.velocity}`);
  },

  onUpdate: (self) => {
    console.log("Position updated (includes momentum)");
  },

  onSnap: (self) => {
    console.log(`Snapped to: ${self.x}, ${self.y}`);
  },

  onSettle: (self) => {
    console.log("Element settled (velocity = 0)");
  },

  onResize: (self) => {
    console.log("Container resizing");
  },

  onAfterResize: (self) => {
    console.log("Container resized");
  },
});

Advanced Patterns

Carousel Slider

createDraggable(".slider-track", {
  container: ".slider",
  axis: "x", // X-axis only
  snapX: (index) => index * 300, // Snap to slide width
  onSnap: (self) => {
    const slideIndex = Math.round(self.x / 300);
    updateSlideIndicator(slideIndex);
  },
});

Custom Momentum

const draggable = createDraggable(".element", {
  maxVelocity: 30,
  minVelocity: 0.5,
  onRelease: (self) => {
    // Velocity automatically applies momentum
    console.log(`Released with velocity: ${self.velocity}`);
  },
  onSettle: (self) => {
    console.log("Momentum stopped");
  },
});

Constrained to Path

createDraggable(".slider-thumb", {
  container: ".slider-track",
  axis: "x", // Only horizontal
  containerPadding: [0, 0, 0, 0],
  onUpdate: (self) => {
    // Update value based on progress
    const value = self.progressX * 100;
    updateSliderValue(value);
  },
});

Throwable Cards

createDraggable(".card", {
  container: ".deck",
  maxVelocity: 80, // Allow fast throws
  onRelease: (self) => {
    if (self.velocity > 50) {
      // High velocity = swipe away
      animateOut(self.$target);
    }
  },
});

Drag with Rotation

createDraggable(".disc", {
  onUpdate: (self) => {
    // Rotate based on drag angle
    self.$target.style.transform = `
      translate(${self.x}px, ${self.y}px)
      rotate(${self.angle}deg)
    `;
  },
});

Types

/**
 * Draggable configuration parameters
 */
interface DraggableParams {
  /** Trigger element selector or element (defaults to target) */
  trigger?: string | HTMLElement;

  /** Container element for boundaries */
  container?: string | HTMLElement | Array<number> | ((draggable: Draggable) => string | HTMLElement | Array<number>);

  /** Enable/disable or configure X-axis dragging */
  x?: boolean | DraggableAxisParam;

  /** Enable/disable or configure Y-axis dragging */
  y?: boolean | DraggableAxisParam;

  /** Value modifier function */
  modifier?: TweenModifier;

  /** Snap points for both axes (grid size or array of positions) */
  snap?: number | Array<number> | ((draggable: Draggable) => number | Array<number>);

  /** Container padding [top, right, bottom, left] or single number for all sides */
  containerPadding?: number | Array<number> | ((draggable: Draggable) => number | Array<number>);

  /** Container edge friction (0-1) during active drag */
  containerFriction?: number | ((draggable: Draggable) => number);

  /** Container edge friction (0-1) during release momentum */
  releaseContainerFriction?: number | ((draggable: Draggable) => number);

  /** X-axis snap points (grid size or array of positions) */
  snapX?: number | Array<number>;

  /** Y-axis snap points (grid size or array of positions) */
  snapY?: number | Array<number>;

  /** Drag speed multiplier */
  dragSpeed?: number | ((draggable: Draggable) => number);

  /** Minimum movement to activate drag (pixels) */
  dragThreshold?: number | DraggableDragThresholdParams | ((draggable: Draggable) => number | DraggableDragThresholdParams);

  /** Auto-scroll speed when dragging near edges */
  scrollSpeed?: number | ((draggable: Draggable) => number);

  /** Distance from edge to trigger auto-scroll (pixels) */
  scrollThreshold?: number | ((draggable: Draggable) => number);

  /** Maximum velocity */
  maxVelocity?: number | ((draggable: Draggable) => number);

  /** Minimum velocity before stopping */
  minVelocity?: number | ((draggable: Draggable) => number);

  /** Velocity multiplier for momentum */
  velocityMultiplier?: number | ((draggable: Draggable) => number);

  /** Spring mass for release physics */
  releaseMass?: number;

  /** Spring stiffness for release physics */
  releaseStiffness?: number;

  /** Spring damping for release physics */
  releaseDamping?: number;

  /** Easing function for release animation */
  releaseEase?: EasingParam;

  /** Cursor styling configuration */
  cursor?: boolean | DraggableCursorParams | ((draggable: Draggable) => boolean | DraggableCursorParams);

  /** Called on grab */
  onGrab?: Callback<Draggable>;

  /** Called during drag */
  onDrag?: Callback<Draggable>;

  /** Called on release */
  onRelease?: Callback<Draggable>;

  /** Called on update */
  onUpdate?: Callback<Draggable>;

  /** Called when settled */
  onSettle?: Callback<Draggable>;

  /** Called on snap */
  onSnap?: Callback<Draggable>;

  /** Called on resize */
  onResize?: Callback<Draggable>;

  /** Called after resize */
  onAfterResize?: Callback<Draggable>;
}

/**
 * Axis-specific dragging configuration
 */
interface DraggableAxisParam {
  /** Property to map axis value to */
  mapTo?: string;

  /** Value modifier function */
  modifier?: TweenModifier;

  /** Composition mode for transforms */
  composition?: TweenComposition;

  /** Snap points for this axis */
  snap?: number | Array<number> | ((draggable: Draggable) => number | Array<number>);
}

/**
 * Drag threshold configuration by input type
 */
interface DraggableDragThresholdParams {
  /** Drag threshold for mouse input (pixels) */
  mouse?: number;

  /** Drag threshold for touch input (pixels) */
  touch?: number;
}

/**
 * Cursor configuration
 */
interface DraggableCursorParams {
  /** Cursor style when hovering (not grabbed) */
  onHover?: string;

  /** Cursor style when grabbed/dragging */
  onGrab?: string;
}

/**
 * Callback function type
 */
type Callback<T> = (instance: T) => void;

/**
 * Tween modifier function type
 */
type TweenModifier = (value: any) => any;

/**
 * Tween composition mode type
 */
type TweenComposition = "none" | "replace" | "blend";