UNPKG

motion-v

Version:

<h1 align="center"> <img width="35" height="35" alt="Motion logo" src="https://github.com/user-attachments/assets/00d6d1c3-72c4-4c2f-a664-69da13182ffc" /><br />Motion for Vue</h1>

194 lines (193 loc) 6.7 kB
import { isPrimaryPointer } from "../../../events/utils/is-primary-pointer.mjs"; import { extractEventInfo } from "../../../events/event-info.mjs"; import { addPointerEvent } from "../../../events/add-pointer-event.mjs"; import { cancelFrame, distance2D, frame, frameData } from "framer-motion/dom"; import { millisecondsToSeconds, pipe, secondsToMilliseconds } from "motion-utils"; var overflowStyles = /* @__PURE__ */ new Set(["auto", "scroll"]); var PanSession = class { constructor(event, handlers, { transformPagePoint, contextWindow, dragSnapToOrigin = false, element } = {}) { this.startEvent = null; this.lastMoveEvent = null; this.lastMoveEventInfo = null; this.handlers = {}; this.contextWindow = window; this.scrollPositions = /* @__PURE__ */ new Map(); this.onElementScroll = (event$1) => { this.handleScroll(event$1.target); }; this.onWindowScroll = () => { this.handleScroll(window); }; this.updatePoint = () => { if (!(this.lastMoveEvent && this.lastMoveEventInfo)) return; const info = getPanInfo(this.lastMoveEventInfo, this.history); const isPanStarted = this.startEvent !== null; const isDistancePastThreshold = distance2D(info.offset, { x: 0, y: 0 }) >= 3; if (!isPanStarted && !isDistancePastThreshold) return; const { point: point$1 } = info; const { timestamp: timestamp$1 } = frameData; this.history.push({ ...point$1, timestamp: timestamp$1 }); const { onStart, onMove } = this.handlers; if (!isPanStarted) { onStart && onStart(this.lastMoveEvent, info); this.startEvent = this.lastMoveEvent; } onMove && onMove(this.lastMoveEvent, info); }; this.handlePointerMove = (event$1, info) => { this.lastMoveEvent = event$1; this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint); frame.update(this.updatePoint, true); }; this.handlePointerUp = (event$1, info) => { this.end(); const { onEnd, onSessionEnd, resumeAnimation } = this.handlers; if (this.dragSnapToOrigin || !this.startEvent) resumeAnimation && resumeAnimation(); if (!(this.lastMoveEvent && this.lastMoveEventInfo)) return; const panInfo = getPanInfo(event$1.type === "pointercancel" ? this.lastMoveEventInfo : transformPoint(info, this.transformPagePoint), this.history); if (this.startEvent && onEnd) onEnd(event$1, panInfo); onSessionEnd && onSessionEnd(event$1, panInfo); }; if (!isPrimaryPointer(event)) return; this.dragSnapToOrigin = dragSnapToOrigin; this.handlers = handlers; this.transformPagePoint = transformPagePoint; this.contextWindow = contextWindow || window; const initialInfo = transformPoint(extractEventInfo(event), this.transformPagePoint); const { point } = initialInfo; const { timestamp } = frameData; this.history = [{ ...point, timestamp }]; const { onSessionStart } = handlers; onSessionStart && onSessionStart(event, getPanInfo(initialInfo, this.history)); this.removeListeners = pipe(addPointerEvent(this.contextWindow, "pointermove", this.handlePointerMove), addPointerEvent(this.contextWindow, "pointerup", this.handlePointerUp), addPointerEvent(this.contextWindow, "pointercancel", this.handlePointerUp)); if (element) this.startScrollTracking(element); } isScrollable(node) { const style = window.getComputedStyle(node); return style.overflow === "auto" || style.overflow === "scroll" || style.overflowX === "auto" || style.overflowX === "scroll" || style.overflowY === "auto" || style.overflowY === "scroll"; } startScrollTracking(element) { let current = element.parentElement; while (current) { const style = getComputedStyle(current); if (overflowStyles.has(style.overflowX) || overflowStyles.has(style.overflowY)) this.scrollPositions.set(current, { x: current.scrollLeft, y: current.scrollTop }); current = current.parentElement; } this.scrollPositions.set(window, { x: window.scrollX, y: window.scrollY }); window.addEventListener("scroll", this.onElementScroll, { capture: true, passive: true }); window.addEventListener("scroll", this.onWindowScroll, { passive: true }); this.removeScrollListeners = () => { window.removeEventListener("scroll", this.onElementScroll, { capture: true }); window.removeEventListener("scroll", this.onWindowScroll); }; } handleScroll(target) { const initial = this.scrollPositions.get(target); if (!initial) return; const isWindow = target === window; const current = isWindow ? { x: window.scrollX, y: window.scrollY } : { x: target.scrollLeft, y: target.scrollTop }; const delta = { x: current.x - initial.x, y: current.y - initial.y }; if (delta.x === 0 && delta.y === 0) return; if (isWindow) { if (this.lastMoveEventInfo) { this.lastMoveEventInfo.point.x += delta.x; this.lastMoveEventInfo.point.y += delta.y; } } else if (this.history.length > 0) { this.history[0].x -= delta.x; this.history[0].y -= delta.y; } this.scrollPositions.set(target, current); frame.update(this.updatePoint, true); } updateHandlers(handlers) { this.handlers = handlers; } end() { this.removeListeners && this.removeListeners(); this.removeScrollListeners?.(); this.scrollPositions.clear(); cancelFrame(this.updatePoint); } }; function transformPoint(info, transformPagePoint) { return transformPagePoint ? { point: transformPagePoint(info.point) } : info; } function subtractPoint(a, b) { return { x: a.x - b.x, y: a.y - b.y }; } function getPanInfo({ point }, history) { return { point, delta: subtractPoint(point, lastDevicePoint(history)), offset: subtractPoint(point, startDevicePoint(history)), velocity: getVelocity(history, .1) }; } function startDevicePoint(history) { return history[0]; } function lastDevicePoint(history) { return history[history.length - 1]; } function getVelocity(history, timeDelta) { if (history.length < 2) return { x: 0, y: 0 }; let i = history.length - 1; let timestampedPoint = null; const lastPoint = lastDevicePoint(history); while (i >= 0) { timestampedPoint = history[i]; if (lastPoint.timestamp - timestampedPoint.timestamp > secondsToMilliseconds(timeDelta)) break; i--; } if (!timestampedPoint) return { x: 0, y: 0 }; const time = millisecondsToSeconds(lastPoint.timestamp - timestampedPoint.timestamp); if (time === 0) return { x: 0, y: 0 }; const currentVelocity = { x: (lastPoint.x - timestampedPoint.x) / time, y: (lastPoint.y - timestampedPoint.y) / time }; if (currentVelocity.x === Infinity) currentVelocity.x = 0; if (currentVelocity.y === Infinity) currentVelocity.y = 0; return currentVelocity; } export { PanSession };