UNPKG

@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

66 lines (65 loc) 2.67 kB
/** * Linearly interpolates between `from` and `to` by `t` [0,1]. * Preserves float precision. */ export const mixNumber = (from, to, t) => from + (to - from) * t; /** * Apply float-safe constraints with optional elastic mixing. * Mirrors Framer Motion behavior: clamp via Math.min/Math.max with no rounding. * If `elastic` provided, blends toward the bound using its side-specific factor. * * @param point The unconstrained value to clamp. * @param range Min/max boundaries. Either or both may be omitted. * @param elastic Optional per-side elastic factor(s) in [0,1]. When provided, * the value is interpolated toward the boundary instead of hard-clamped. * @returns The constrained (and optionally elastically blended) value. * * @example * ```ts * // Hard clamp to [0, 100] * applyConstraints(120, { min: 0, max: 100 }) // 100 * * // Elastic overshoot (50 % blend toward max) * applyConstraints(120, { min: 0, max: 100 }, 0.5) // 110 * ``` */ export const applyConstraints = (point, range, elastic) => { const hasMin = range.min !== undefined; const hasMax = range.max !== undefined; if (hasMin && point < range.min) { if (elastic) { const e = typeof elastic === 'number' ? { min: elastic, max: elastic } : elastic; return mixNumber(range.min, point, Math.max(0, Math.min(1, e.min))); } return Math.max(point, range.min); } else if (hasMax && point > range.max) { if (elastic) { const e = typeof elastic === 'number' ? { min: elastic, max: elastic } : elastic; return mixNumber(range.max, point, Math.max(0, Math.min(1, e.max))); } return Math.min(point, range.max); } return point; }; /** * Parse a CSS `matrix(a, b, c, d, tx, ty)` string into translate components. * * Used to read the rendered translate after Motion writes inline transforms * during drag-cancel hooks. * * @param transform The computed `transform` style — typically `'none'` or * `'matrix(1, 0, 0, 1, 10, 20)'`. `matrix3d(...)` is not supported. * @returns The 2D translate components `{ tx, ty }`. Defaults to * `{ tx: 0, ty: 0 }` for `'none'` or any non-matching input. * @example * parseMatrixTranslate('matrix(1, 0, 0, 1, 10, 20)') // → { tx: 10, ty: 20 } * parseMatrixTranslate('none') // → { tx: 0, ty: 0 } */ export const parseMatrixTranslate = (transform) => { const m = transform.match(/matrix\(([^)]+)\)/); if (!m) return { tx: 0, ty: 0 }; const parts = m[1].split(',').map((s) => Number.parseFloat(s.trim())); return { tx: parts[4] ?? 0, ty: parts[5] ?? 0 }; };