@humanspeak/svelte-motion
Version:
Framer Motion for Svelte 5. Declarative motion.<tag> components with AnimatePresence exit animations, gestures (hover, tap, drag, focus, in-view), variants, FLIP layout animations, shared-layout transitions, spring physics, and scroll-linked motion values
121 lines (120 loc) • 4.96 kB
TypeScript
import type { DragAxis, DragConstraints, DragControls, DragInfo, MotionWhileDrag } from '../types';
import { type AnimationOptions } from 'motion';
/**
* Options passed to `attachDrag`. These are derived from `MotionProps` and pre-merged
* with motion config in `_MotionContainer.svelte`.
*
* Note: `mergedTransition` should be the already-resolved transition that combines
* MotionConfig context and the component's `transition` prop.
*/
export type AttachDragOptions = {
axis: DragAxis;
constraints?: DragConstraints;
elastic?: number;
momentum?: boolean;
transition?: {
bounceStiffness?: number;
bounceDamping?: number;
power?: number;
timeConstant?: number;
min?: number;
max?: number;
};
directionLock?: boolean;
listener?: boolean;
controls?: DragControls | undefined;
whileDrag?: MotionWhileDrag;
mergedTransition: AnimationOptions;
callbacks?: {
onStart?: (e: PointerEvent, info: DragInfo) => void;
onMove?: (e: PointerEvent, info: DragInfo) => void;
onEnd?: (e: PointerEvent, info: DragInfo) => void;
onDirectionLock?: (axis: 'x' | 'y') => void;
onTransitionEnd?: () => void;
};
baselineSources?: {
initial?: Record<string, unknown>;
animate?: Record<string, unknown>;
};
propagation?: boolean;
snapToOrigin?: boolean;
};
/**
* Resolve drag constraints into pixel offsets relative to the dragged element's origin.
*
* - HTMLElement: Constrains the element to the bounding box of the provided element.
* - Pixel object: Direct pixel limits for top/left/right/bottom.
*/
/**
* Normalize constraints to pixel offsets relative to the dragged element's origin.
*
* HTMLElement constraints: allow moving within the container bounds (subtractive rect math).
* Pixel object: direct min/max per side.
*/
export declare const resolveConstraints: (el: HTMLElement | null, constraints: DragConstraints | undefined) => {
top: number;
left: number;
right: number;
bottom: number;
} | null;
/**
* Apply elastic overflow outside of [min, max] using a linear ratio.
*/
/**
* Apply elastic overflow outside the [min, max] range.
* When beyond bounds, the extra distance is scaled linearly by `elastic`.
*/
export declare const applyElastic: (value: number, min: number, max: number, elastic: number) => number;
/**
* Cleanup handle returned by {@link attachDrag}. Invoke it to detach the
* gesture's pointer/resize listeners. It does NOT cancel an in-flight
* momentum/settle animation — matching framer-motion, whose drag teardown
* removes listeners only and deliberately lets motion continue across an
* unmount/remount (e.g. reorder reconciliation); the animation is cleaned
* up by the element/motion-value lifecycle.
*
* The handle also carries `adjustOrigin(dx, dy)`, which shifts the LIVE
* gesture's origin + visual offset by a layout-shift delta mid-drag — used
* for projection-driven cursor pinning when a layout slot moves under the
* dragged element (#379 / #310). It is a no-op when not currently dragging
* and compensates on both axes (mirroring upstream's per-axis `eachAxis`
* compensation).
*/
export type AttachDragCleanup = (() => void) & {
adjustOrigin: (dx: number, dy: number) => void;
};
/**
* Attach a drag gesture to an element.
*
* Captures the pointer and updates x/y transforms with axis and optional
* direction lock, applies elastic overflow against constraints, emits
* lifecycle callbacks with `DragInfo`, and runs a momentum animation on
* release when enabled.
*
* Lifecycle:
* - pointerdown → capture pointer, snapshot origin, start velocity history, enter whileDrag
* - pointermove → compute deltas, direction lock, apply constraints + elastic, write x/y
* - pointerup/cancel → either run momentum decay to a target or settle/clamp instantly
*
* Invariant: `applied` tracks the currently applied x/y transform — it
* must stay in sync when writing transforms or finishing animations, or a
* second drag "jumps" from a stale origin (commonly a missed update after
* a non-zero-duration settle animation).
*
* @param el The element to make draggable.
* @param opts Drag options — `axis`, `constraints`, `elastic`,
* `momentum`, `whileDrag`, and the `onDrag*` lifecycle callbacks.
* @returns A callable cleanup handle ({@link AttachDragCleanup}): call it
* to detach the gesture's listeners (in-flight momentum is not
* cancelled — see the type docs), or call its `adjustOrigin(dx, dy)` to
* reposition the live gesture mid-drag.
* @example
* ```ts
* const cleanup = attachDrag(el, { axis: 'x', momentum: true })
* // …when a layout swap shifts the slot under the cursor mid-drag:
* cleanup.adjustOrigin(10, -5)
* // on teardown:
* cleanup()
* ```
*/
export declare const attachDrag: (el: HTMLElement, opts: AttachDragOptions) => AttachDragCleanup;