svelte-motion
Version:
Svelte animation library based on the React library framer-motion.
158 lines (155 loc) • 6.02 kB
JavaScript
/**
based on framer-motion@4.0.3,
Copyright (c) 2018 Framer B.V.
*/
import {fixed} from '../utils/fix-process-env';
import { isMouseEvent, isTouchEvent } from './utils/event-type.js';
import { extractEventInfo } from '../events/event-info.js';
import sync, { getFrameData, cancelSync } from 'framesync';
import { secondsToMilliseconds } from '../utils/time-conversion.js';
import { addPointerEvent } from '../events/use-pointer-event.js';
import { distance, pipe } from 'popmotion';
/**
* @internal
*/
var PanSession = /** @class */ (function () {
function PanSession(event, handlers, _a) {
var _this = this;
var _b = _a === void 0 ? {} : _a, transformPagePoint = _b.transformPagePoint;
/**
* @internal
*/
this.startEvent = null;
/**
* @internal
*/
this.lastMoveEvent = null;
/**
* @internal
*/
this.lastMoveEventInfo = null;
/**
* @internal
*/
this.handlers = {};
this.updatePoint = function () {
if (!(_this.lastMoveEvent && _this.lastMoveEventInfo))
return;
var info = getPanInfo(_this.lastMoveEventInfo, _this.history);
var isPanStarted = _this.startEvent !== null;
// Only start panning if the offset is larger than 3 pixels. If we make it
// any larger than this we'll want to reset the pointer history
// on the first update to avoid visual snapping to the cursoe.
var isDistancePastThreshold = distance(info.offset, { x: 0, y: 0 }) >= 3;
if (!isPanStarted && !isDistancePastThreshold)
return;
var point = info.point;
var timestamp = getFrameData().timestamp;
_this.history.push(Object.assign(Object.assign({}, point), { timestamp: timestamp }));
var _a = _this.handlers, onStart = _a.onStart, onMove = _a.onMove;
if (!isPanStarted) {
onStart && onStart(_this.lastMoveEvent, info);
_this.startEvent = _this.lastMoveEvent;
}
onMove && onMove(_this.lastMoveEvent, info);
};
this.handlePointerMove = function (event, info) {
_this.lastMoveEvent = event;
_this.lastMoveEventInfo = transformPoint(info, _this.transformPagePoint);
// Because Safari doesn't trigger mouseup events when it's above a `<select>`
if (isMouseEvent(event) && event.buttons === 0) {
_this.handlePointerUp(event, info);
return;
}
// Throttle mouse move event to once per frame
sync.update(_this.updatePoint, true);
};
this.handlePointerUp = function (event, info) {
_this.end();
var _a = _this.handlers, onEnd = _a.onEnd, onSessionEnd = _a.onSessionEnd;
var panInfo = getPanInfo(transformPoint(info, _this.transformPagePoint), _this.history);
if (_this.startEvent && onEnd) {
onEnd(event, panInfo);
}
onSessionEnd && onSessionEnd(event, panInfo);
};
// If we have more than one touch, don't start detecting this gesture
if (isTouchEvent(event) && event.touches.length > 1)
return;
this.handlers = handlers;
this.transformPagePoint = transformPagePoint;
var info = extractEventInfo(event);
var initialInfo = transformPoint(info, this.transformPagePoint);
var point = initialInfo.point;
var timestamp = getFrameData().timestamp;
this.history = [Object.assign(Object.assign({}, point), { timestamp: timestamp })];
var onSessionStart = handlers.onSessionStart;
onSessionStart &&
onSessionStart(event, getPanInfo(initialInfo, this.history));
this.removeListeners = pipe(addPointerEvent(window, "pointermove", this.handlePointerMove), addPointerEvent(window, "pointerup", this.handlePointerUp), addPointerEvent(window, "pointercancel", this.handlePointerUp));
}
PanSession.prototype.updateHandlers = function (handlers) {
this.handlers = handlers;
};
PanSession.prototype.end = function () {
this.removeListeners && this.removeListeners();
cancelSync.update(this.updatePoint);
};
return PanSession;
}());
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(_a, history) {
var point = _a.point;
return {
point: point,
delta: subtractPoint(point, lastDevicePoint(history)),
offset: subtractPoint(point, startDevicePoint(history)),
velocity: getVelocity(history, 0.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 };
}
var i = history.length - 1;
var timestampedPoint = null;
var 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 };
}
var time = (lastPoint.timestamp - timestampedPoint.timestamp) / 1000;
if (time === 0) {
return { x: 0, y: 0 };
}
var 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 };