or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

animation-controls.mdconfiguration.mddom-utilities.mdgestures.mdindex.mdlayout-presence.mdmotion-components.mdmotion-values.mdscroll-view.md
tile.json

gestures.mddocs/

Gestures

Drag controls and gesture event handlers for interactive animations and user input handling.

Capabilities

Drag Controls Hook

Creates controls for programmatically managing drag operations.

/**
 * Creates drag controls for manual drag management
 * @returns DragControls instance
 */
function useDragControls(): DragControls;

interface DragControls {
  /**
   * Start a drag operation
   * @param event - Pointer event that triggered the drag
   * @param options - Drag start options
   */
  start(event: React.PointerEvent | PointerEvent, options?: StartOptions): void;
  
  /**
   * Cancel the current drag operation
   */
  cancel(): void;
  
  /**
   * Stop the current drag operation
   */
  stop(): void;
  
  /**
   * Subscribe to drag controls updates
   * @param controls - Another drag controls instance to link
   */
  subscribe(controls: DragControls): () => void;
}

interface StartOptions {
  /**
   * Snap back to original position when drag ends
   */
  snapToCursor?: boolean;
  
  /**
   * Cursor to use during drag
   */
  cursorProgress?: Point;
}

interface Point {
  x: number;
  y: number;
}

Usage Examples:

import { motion, useDragControls } from "framer-motion";
import { useRef } from "react";

// Basic drag controls
function DragControlsExample() {
  const controls = useDragControls();
  
  return (
    <div>
      <div
        className="w-8 h-8 bg-blue-500 cursor-grab rounded-full mb-4"
        onPointerDown={(e) => controls.start(e)}
      >
        Drag Handle
      </div>
      
      <motion.div
        drag
        dragControls={controls}
        className="w-24 h-24 bg-red-500 rounded"
      >
        Draggable Element
      </motion.div>
    </div>
  );
}

// Multiple linked drag controls
function LinkedDragExample() {
  const controls1 = useDragControls();
  const controls2 = useDragControls();
  
  // Link controls so they move together
  React.useEffect(() => {
    const unsubscribe = controls1.subscribe(controls2);
    return unsubscribe;
  }, [controls1, controls2]);
  
  return (
    <div className="space-y-4">
      <motion.div
        drag
        dragControls={controls1}
        className="w-24 h-24 bg-blue-500 rounded"
      >
        Element 1
      </motion.div>
      
      <motion.div
        drag
        dragControls={controls2}
        className="w-24 h-24 bg-green-500 rounded"
      >
        Element 2 (linked)
      </motion.div>
    </div>
  );
}

Drag Props and Events

Motion components support comprehensive drag functionality through props.

interface DragProps {
  /**
   * Enable dragging on x, y, or both axes
   */
  drag?: boolean | "x" | "y";
  
  /**
   * Drag controls instance for manual control
   */
  dragControls?: DragControls;
  
  /**
   * Constraints for drag boundaries
   */
  dragConstraints?: Partial<BoundingBox2D> | React.RefObject<Element>;
  
  /**
   * Enable elastic drag beyond constraints
   */
  dragElastic?: boolean | number;
  
  /**
   * Enable momentum and inertia after drag ends
   */
  dragMomentum?: boolean;
  
  /**
   * Transition for snapping back to constraints
   */
  dragTransition?: {
    bounceStiffness?: number;
    bounceDamping?: number;
    power?: number;
    timeConstant?: number;
    restDelta?: number;
    modifyTarget?: (target: number) => number;
  };
  
  /**
   * Propagate drag to parent elements
   */
  dragPropagation?: boolean;
  
  /**
   * Listen for external drag events
   */
  dragListener?: boolean;
  
  /**
   * Direction lock threshold
   */
  dragDirectionLock?: boolean;
  
  /**
   * Minimum distance before drag starts
   */
  dragSnapToOrigin?: boolean;
}

interface BoundingBox2D {
  top: number;
  right: number;
  bottom: number;
  left: number;
}

Drag Event Handlers:

interface DragEventHandlers {
  /**
   * Called when drag operation starts
   */
  onDragStart?: (
    event: MouseEvent | TouchEvent | PointerEvent,
    info: PanInfo
  ) => void;
  
  /**
   * Called continuously during drag
   */
  onDrag?: (
    event: MouseEvent | TouchEvent | PointerEvent,
    info: PanInfo
  ) => void;
  
  /**
   * Called when drag operation ends
   */
  onDragEnd?: (
    event: MouseEvent | TouchEvent | PointerEvent,
    info: PanInfo
  ) => void;
  
  /**
   * Called when dragging beyond constraints (elastic)
   */
  onDirectionLock?: (axis: "x" | "y") => void;
}

interface PanInfo {
  /**
   * Current pointer position relative to page
   */
  point: Point;
  
  /**
   * Change in position since last event
   */
  delta: Point;
  
  /**
   * Total offset from drag start position
   */
  offset: Point;
  
  /**
   * Current velocity of the pointer
   */
  velocity: Point;
}

Usage Examples:

import { motion } from "framer-motion";
import { useRef, useState } from "react";

// Constrained dragging
function ConstrainedDragExample() {
  const constraintsRef = useRef<HTMLDivElement>(null);
  
  return (
    <div
      ref={constraintsRef}
      className="w-96 h-96 bg-gray-200 relative border-2 border-gray-400"
    >
      <motion.div
        drag
        dragConstraints={constraintsRef}
        dragElastic={0.2}
        className="w-16 h-16 bg-blue-500 rounded cursor-grab"
      >
        Constrained
      </motion.div>
    </div>
  );
}

// Axis-specific dragging with momentum
function AxisDragExample() {
  return (
    <div className="space-y-8">
      <motion.div
        drag="x"
        dragMomentum={true}
        dragConstraints={{ left: 0, right: 300 }}
        className="w-16 h-16 bg-red-500 rounded cursor-grab"
      >
        X-axis only
      </motion.div>
      
      <motion.div
        drag="y"
        dragMomentum={false}
        dragConstraints={{ top: 0, bottom: 200 }}
        className="w-16 h-16 bg-green-500 rounded cursor-grab"
      >
        Y-axis only
      </motion.div>
    </div>
  );
}

// Drag with custom transitions and events
function AdvancedDragExample() {
  const [dragInfo, setDragInfo] = useState<PanInfo | null>(null);
  
  return (
    <div>
      <motion.div
        drag
        dragConstraints={{ left: -200, right: 200, top: -200, bottom: 200 }}
        dragTransition={{
          bounceStiffness: 300,
          bounceDamping: 20,
          power: 0.2,
          modifyTarget: (target) => Math.round(target / 50) * 50 // Snap to grid
        }}
        onDragStart={(event, info) => {
          console.log("Drag started", info);
        }}
        onDrag={(event, info) => {
          setDragInfo(info);
        }}
        onDragEnd={(event, info) => {
          console.log("Drag ended", info);
          setDragInfo(null);
        }}
        className="w-16 h-16 bg-purple-500 rounded cursor-grab"
      >
        Advanced Drag
      </motion.div>
      
      {dragInfo && (
        <div className="mt-4 text-sm">
          <p>Position: {Math.round(dragInfo.point.x)}, {Math.round(dragInfo.point.y)}</p>
          <p>Velocity: {Math.round(dragInfo.velocity.x)}, {Math.round(dragInfo.velocity.y)}</p>
          <p>Offset: {Math.round(dragInfo.offset.x)}, {Math.round(dragInfo.offset.y)}</p>
        </div>
      )}
    </div>
  );
}

Hover Events

Handle mouse hover interactions with motion components.

interface HoverEventHandlers {
  /**
   * Called when mouse enters element
   */
  onHoverStart?: (event: MouseEvent, info: EventInfo) => void;
  
  /**
   * Called when mouse leaves element
   */
  onHoverEnd?: (event: MouseEvent, info: EventInfo) => void;
}

interface EventInfo {
  /**
   * Current pointer position
   */
  point: Point;
}

Usage Example:

import { motion } from "framer-motion";
import { useState } from "react";

function HoverExample() {
  const [hoverInfo, setHoverInfo] = useState<string>("");
  
  return (
    <motion.div
      whileHover={{ scale: 1.1, rotate: 5 }}
      onHoverStart={(event, info) => {
        setHoverInfo(`Hover started at ${info.point.x}, ${info.point.y}`);
      }}
      onHoverEnd={(event, info) => {
        setHoverInfo(`Hover ended at ${info.point.x}, ${info.point.y}`);
      }}
      className="w-32 h-32 bg-blue-500 rounded cursor-pointer flex items-center justify-center text-white"
    >
      Hover me
      {hoverInfo && <div className="absolute top-full mt-2 text-sm text-black">{hoverInfo}</div>}
    </motion.div>
  );
}

Tap Events

Handle tap/click interactions with detailed gesture information.

interface TapEventHandlers {
  /**
   * Called when tap/click occurs
   */
  onTap?: (event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo) => void;
  
  /**
   * Called when tap/click starts (pointer down)
   */
  onTapStart?: (event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo) => void;
  
  /**
   * Called when tap is cancelled (pointer moved too far)
   */
  onTapCancel?: (event: MouseEvent | TouchEvent | PointerEvent, info: TapInfo) => void;
}

interface TapInfo {
  /**
   * Current pointer position
   */
  point: Point;
}

Usage Example:

import { motion } from "framer-motion";
import { useState } from "react";

function TapExample() {
  const [tapCount, setTapCount] = useState(0);
  const [lastTapPoint, setLastTapPoint] = useState<Point | null>(null);
  
  return (
    <motion.button
      whileTap={{ scale: 0.95 }}
      onTap={(event, info) => {
        setTapCount(count => count + 1);
        setLastTapPoint(info.point);
      }}
      onTapStart={(event, info) => {
        console.log("Tap started at", info.point);
      }}
      onTapCancel={(event, info) => {
        console.log("Tap cancelled at", info.point);
      }}
      className="px-6 py-3 bg-green-500 text-white rounded hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-400"
    >
      Tap me (count: {tapCount})
      {lastTapPoint && (
        <div className="text-xs mt-1">
          Last tap: {Math.round(lastTapPoint.x)}, {Math.round(lastTapPoint.y)}
        </div>
      )}
    </motion.button>
  );
}

Focus Events

Handle focus and blur events for accessibility and keyboard navigation.

interface FocusEventHandlers {
  /**
   * Called when element receives focus
   */
  onFocus?: (event: FocusEvent) => void;
  
  /**
   * Called when element loses focus
   */
  onBlur?: (event: FocusEvent) => void;
}

Usage Example:

import { motion } from "framer-motion";
import { useState } from "react";

function FocusExample() {
  const [isFocused, setIsFocused] = useState(false);
  
  return (
    <motion.input
      type="text"
      placeholder="Focus me"
      whileFocus={{ scale: 1.05, borderColor: "#3B82F6" }}
      onFocus={() => setIsFocused(true)}
      onBlur={() => setIsFocused(false)}
      className="px-4 py-2 border-2 border-gray-300 rounded outline-none transition-colors"
      style={{
        borderColor: isFocused ? "#3B82F6" : "#D1D5DB"
      }}
    />
  );
}

DOM Event Utilities

Utilities for handling DOM events in a cross-platform way.

/**
 * Add pointer event listener with proper cross-platform handling
 * @param element - Element to add listener to
 * @param event - Event type to listen for
 * @param handler - Event handler function
 * @param options - Event listener options
 * @returns Cleanup function
 */
function addPointerEvent(
  element: Element,
  event: string,
  handler: (event: PointerEvent) => void,
  options?: AddEventListenerOptions
): () => void;

/**
 * Add pointer info to event handlers for consistent data structure
 * @param handler - Original event handler
 * @returns Enhanced handler with pointer info
 */
function addPointerInfo<T extends Event>(
  handler: (event: T, info: EventInfo) => void
): (event: T) => void;

/**
 * React hook for DOM event handling
 * @param ref - Element reference
 * @param event - Event type
 * @param handler - Event handler
 * @param options - Event options
 */
function useDomEvent(
  ref: React.RefObject<Element>,
  event: string,
  handler: (event: Event) => void,
  options?: AddEventListenerOptions
): void;

Usage Example:

import { useDomEvent } from "framer-motion";
import { useRef, useEffect } from "react";

function DOMEventExample() {
  const ref = useRef<HTMLDivElement>(null);
  
  // React hook approach
  useDomEvent(ref, "pointerdown", (event) => {
    console.log("Pointer down:", event);
  });
  
  // Manual approach
  useEffect(() => {
    const element = ref.current;
    if (!element) return;
    
    const cleanup = addPointerEvent(
      element,
      "pointermove",
      (event) => {
        console.log("Pointer move:", event.clientX, event.clientY);
      },
      { passive: true }
    );
    
    return cleanup;
  }, []);
  
  return (
    <div
      ref={ref}
      className="w-64 h-64 bg-gray-200 border-2 border-gray-400 flex items-center justify-center"
    >
      Interactive area
    </div>
  );
}

Gesture Combination Patterns

Drag with Hover:

<motion.div
  drag
  whileHover={{ scale: 1.05 }}
  whileDrag={{ scale: 1.1, zIndex: 10 }}
  onDragStart={() => console.log("Started dragging")}
  onHoverStart={() => console.log("Started hovering")}
>
  Combined gestures
</motion.div>

Conditional Gestures:

const [isDraggable, setIsDraggable] = useState(true);

<motion.div
  drag={isDraggable}
  dragConstraints={isDraggable ? { left: 0, right: 300 } : false}
  whileTap={!isDraggable ? { scale: 0.95 } : undefined}
  onTap={() => {
    if (!isDraggable) {
      console.log("Clicked instead of dragged");
    }
  }}
>
  Conditional interaction
</motion.div>

Multi-Touch Support:

<motion.div
  drag
  onPanStart={(event, info) => {
    // Handle touch/pan start
    console.log("Pan started with", info.point);
  }}
  onPan={(event, info) => {
    // Handle continuous touch/pan
    console.log("Panning:", info.delta);
  }}
  onPanEnd={(event, info) => {
    // Handle touch/pan end
    console.log("Pan ended with velocity:", info.velocity);
  }}
>
  Multi-touch support
</motion.div>