or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-plugins.mdanimation-control.mdcore-animation.mdcss-properties.mddraggable.mdindex.mdscroll-trigger.mdsvg-animation.mdtext-animation.mdtimeline-system.mdutility-functions.md
tile.json

draggable.mddocs/

Draggable

Draggable adds drag and drop functionality to elements with momentum, bounds checking, collision detection, and smooth interactions. It integrates seamlessly with GSAP animations.

Setup

import { Draggable } from "gsap/Draggable";
gsap.registerPlugin(Draggable);

Capabilities

Creating Draggable Instances

Create draggable elements with comprehensive configuration options.

/**
 * Create Draggable instances for elements
 * @param targets - Elements to make draggable
 * @param vars - Draggable configuration
 * @returns Array of Draggable instances
 */
Draggable.create(targets: string | Element | Element[], vars?: Draggable.Vars): Draggable[];

interface Draggable.Vars {
  // Drag type
  type?: "x" | "y" | "rotation" | "top,left" | "x,y";
  
  // Bounds
  bounds?: Element | string | { minX?: number; maxX?: number; minY?: number; maxY?: number; };
  
  // Behavior
  edgeResistance?: number;     // Resistance when hitting bounds (0-1)
  throwResistance?: number;    // Momentum decay resistance (0-10000)
  throwProps?: boolean;        // Enable momentum throwing
  
  // Snapping
  snap?: object | Function;    // Snap configuration
  
  // Interaction
  trigger?: Element | string;  // Element that triggers drag (if different from target)
  cursor?: string;            // CSS cursor during drag
  zIndexBoost?: boolean;      // Boost z-index during drag
  
  // Callbacks
  onPress?: Function;         // Called when press starts
  onDragStart?: Function;     // Called when drag starts
  onDrag?: Function;          // Called during drag
  onDragEnd?: Function;       // Called when drag ends
  onRelease?: Function;       // Called when released
  onThrowUpdate?: Function;   // Called during momentum throw
  onThrowComplete?: Function; // Called when throw completes
}

Usage Examples:

// Basic draggable
Draggable.create(".box", {
  type: "x,y"  // Allow dragging in both directions
});

// Constrained dragging
Draggable.create(".slider-handle", {
  type: "x",                    // Horizontal only
  bounds: ".slider-track",      // Constrain to track element
  edgeResistance: 0.8
});

// With momentum
Draggable.create(".card", {
  type: "x,y",
  throwProps: true,
  bounds: window,
  edgeResistance: 0.65,
  throwResistance: 300
});

Instance Properties

Properties available on Draggable instances for state and position information.

interface Draggable {
  // Position
  x: number;              // Current x position
  y: number;              // Current y position
  rotation: number;       // Current rotation (for rotation type)
  
  // Drag state
  isDragging: boolean;    // Currently being dragged
  isPressed: boolean;     // Mouse/touch is pressed
  isThrowing: boolean;    // Currently in momentum throw
  
  // Start position
  startX: number;         // X position when drag started
  startY: number;         // Y position when drag started
  
  // Deltas
  deltaX: number;         // Change in X since drag start
  deltaY: number;         // Change in Y since drag start
  
  // Pointer info
  pointerX: number;       // Current pointer X position
  pointerY: number;       // Current pointer Y position
  pointerEvent: Event;    // Current pointer event
  
  // Target info
  target: Element;        // The draggable element
  
  // Methods
  disable(): void;        // Disable dragging
  enable(): void;         // Enable dragging
  endDrag(): void;        // Force end current drag
  kill(): void;           // Destroy the Draggable
  update(): void;         // Update bounds and calculations
}

Usage Examples:

const draggable = Draggable.create(".box")[0];

// Access position
console.log(`Position: ${draggable.x}, ${draggable.y}`);

// Check state
if (draggable.isDragging) {
  console.log("Currently dragging");
}

// Control draggable
draggable.disable();  // Temporarily disable
draggable.enable();   // Re-enable
draggable.kill();     // Clean up when done

Static Methods

Utility methods available on the Draggable class.

/**
 * Get existing Draggable instance for an element
 * @param target - Element to get Draggable for
 * @returns Draggable instance or null
 */
Draggable.get(target: Element): Draggable | null;

/**
 * Hit test between two objects
 * @param obj1 - First object (Element or object with x,y,width,height)
 * @param obj2 - Second object
 * @param threshold - Overlap threshold (0-100, as percentage)
 * @returns True if objects overlap
 */
Draggable.hitTest(obj1: any, obj2: any, threshold?: number): boolean;

/**
 * Get time since last drag ended (in milliseconds)
 * @returns Time since last drag
 */
Draggable.timeSinceDrag(): number;

Usage Examples:

// Get existing Draggable
const existingDraggable = Draggable.get(".box");
if (existingDraggable) {
  existingDraggable.disable();
}

// Collision detection
const box1 = document.querySelector(".box1");
const box2 = document.querySelector(".box2");
if (Draggable.hitTest(box1, box2, 50)) {
  console.log("Boxes are overlapping by at least 50%");
}

// Check recent drag activity
if (Draggable.timeSinceDrag() < 1000) {
  console.log("Something was dragged recently");
}

Drag Types

Different types of dragging behavior available.

type DragType = 
  | "x"          // Horizontal dragging only
  | "y"          // Vertical dragging only  
  | "x,y"        // Free dragging in both directions
  | "top,left"   // Drag using CSS top/left instead of transforms
  | "rotation";  // Rotational dragging around center point

Usage Examples:

// Horizontal slider
Draggable.create(".h-slider", { type: "x" });

// Vertical slider  
Draggable.create(".v-slider", { type: "y" });

// Free movement
Draggable.create(".free-box", { type: "x,y" });

// Rotational knob
Draggable.create(".knob", { 
  type: "rotation",
  bounds: { minRotation: 0, maxRotation: 270 }
});

// Layout properties (for special cases)
Draggable.create(".positioned", { type: "top,left" });

Bounds and Constraints

Configure boundaries and movement constraints.

interface BoundsConfig {
  // Element bounds
  bounds?: Element | string;  // Constrain within element
  
  // Numeric bounds
  bounds?: {
    minX?: number;
    maxX?: number;
    minY?: number;
    maxY?: number;
    minRotation?: number;
    maxRotation?: number;
  };
  
  // Special bounds
  bounds?: Window;            // Constrain to viewport
}

Usage Examples:

// Constrain to parent element
Draggable.create(".child", {
  type: "x,y",
  bounds: ".parent"
});

// Numeric constraints
Draggable.create(".constrained", {
  type: "x,y",
  bounds: { minX: 0, maxX: 500, minY: 0, maxY: 300 }
});

// Viewport bounds
Draggable.create(".viewport-bound", {
  type: "x,y",
  bounds: window
});

// Rotation bounds
Draggable.create(".dial", {
  type: "rotation",
  bounds: { minRotation: -180, maxRotation: 180 }
});

Snapping

Configure snapping behavior for precise positioning.

interface SnapConfig {
  snap?: {
    x?: number | number[] | Function;  // X snap increments/positions
    y?: number | number[] | Function;  // Y snap increments/positions
    rotation?: number | number[];      // Rotation snap increments
  };
}

Usage Examples:

// Grid snapping
Draggable.create(".grid-item", {
  type: "x,y",
  snap: {
    x: 50,    // Snap to multiples of 50px
    y: 50
  }
});

// Specific positions
Draggable.create(".card", {
  type: "x,y", 
  snap: {
    x: [0, 100, 200, 300],  // Snap to specific X positions
    y: [0, 150, 300]        // Snap to specific Y positions
  }
});

// Function-based snapping
Draggable.create(".custom-snap", {
  type: "x,y",
  snap: {
    x: function(endValue) {
      return Math.round(endValue / 25) * 25;  // Custom snap logic
    }
  }
});

Event Callbacks

Handle drag lifecycle events for interactive feedback.

interface DragCallbacks {
  onPress?: (event: Event) => void;           // Mouse/touch press
  onDragStart?: (event: Event) => void;       // Drag starts (after threshold)
  onDrag?: (event: Event) => void;            // During drag
  onDragEnd?: (event: Event) => void;         // Drag ends
  onRelease?: (event: Event) => void;         // Mouse/touch release
  onThrowUpdate?: (event: Event) => void;     // During momentum
  onThrowComplete?: (event: Event) => void;   // Momentum complete
}

Usage Examples:

Draggable.create(".interactive", {
  type: "x,y",
  onPress: function(event) {
    gsap.to(this.target, { scale: 1.1, duration: 0.1 });
  },
  onDragStart: function(event) {
    console.log("Drag started");
    gsap.to(this.target, { rotation: 5, duration: 0.2 });
  },
  onDrag: function(event) {
    // Update other elements based on drag position
    gsap.set(".shadow", { x: this.x + 10, y: this.y + 10 });
  },
  onDragEnd: function(event) {
    gsap.to(this.target, { scale: 1, rotation: 0, duration: 0.3 });
  },
  onRelease: function(event) {
    console.log("Released");
  }
});

Integration with GSAP Animations

Combining Draggable with GSAP animations for enhanced interactions.

// Animate to position after drag
const draggable = Draggable.create(".box", {
  type: "x,y",
  onDragEnd: function() {
    // Animate to nearest corner
    const corners = [
      { x: 0, y: 0 },
      { x: 300, y: 0 },
      { x: 300, y: 200 },
      { x: 0, y: 200 }
    ];
    
    let closest = corners[0];
    let minDistance = Infinity;
    
    corners.forEach(corner => {
      const distance = Math.sqrt(
        Math.pow(this.x - corner.x, 2) + 
        Math.pow(this.y - corner.y, 2)
      );
      if (distance < minDistance) {
        minDistance = distance;
        closest = corner;
      }
    });
    
    gsap.to(this.target, {
      x: closest.x,
      y: closest.y,
      duration: 0.5,
      ease: "back.out(1.7)"
    });
  }
});

// Disable dragging during animation
const animateAndDisable = () => {
  draggable[0].disable();
  gsap.to(".box", {
    x: 200,
    y: 100,
    duration: 1,
    onComplete: () => draggable[0].enable()
  });
};