vevet
Version:
Vevet is a JavaScript library for creative development that simplifies crafting rich interactions like split text animations, carousels, marquees, preloading, and more.
545 lines • 19.8 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Swipe = void 0;
var base_1 = require("../../base");
var initVevet_1 = require("../../global/initVevet");
var utils_1 = require("../../utils");
var Pointers_1 = require("../Pointers");
var Timeline_1 = require("../Timeline");
var Coords_1 = require("./Coords");
var Inertia_1 = require("./Inertia");
var props_1 = require("./props");
var Styles_1 = require("./Styles");
__exportStar(require("./types"), exports);
__exportStar(require("./global"), exports);
/**
* Manages swipe interactions:
* - Tracks movement and detects direction
* - Emits events on start, move, and end
* - Supports exponential inertia
* - Optional bounds with rubber-band overflow and bounce-back
*
* Notes:
* - Does not transform elements, only computes coordinates.
*
* [Documentation](https://vevetjs.com/docs/Swipe)
*
* @group Components
*/
var Swipe = /** @class */ (function (_super) {
__extends(Swipe, _super);
function Swipe(props, onCallbacks) {
var _this = _super.call(this, props, onCallbacks) || this;
/** If swiping has started */
_this._isSwiping = false;
/** If swiping has been aborted */
_this._isAborted = false;
var _a = _this.props, container = _a.container, thumb = _a.thumb, buttons = _a.buttons, pointers = _a.pointers;
_this._coords = new Coords_1.SwipeCoords({
container: container,
props: _this.props,
hasInertia: function () { return _this.hasInertia; },
recalculateBoundsOnInertia: function () { return _this.props.recalculateBoundsOnInertia; },
});
_this._styles = new Styles_1.SwipeStyles(_this);
_this._inertia = new Inertia_1.SwipeInertia({
props: _this.props,
coords: _this._coords,
onStart: function () {
_this._coords.syncTempAngle();
_this.callbacks.emit('inertiaStart', undefined);
},
onFail: function () { return _this.callbacks.emit('inertiaFail', undefined); },
onCancel: function () { return _this.callbacks.emit('inertiaCancel', undefined); },
onEnd: function () { return _this.callbacks.emit('inertiaEnd', undefined); },
});
// create pointers
_this._pointers = new Pointers_1.Pointers({
container: thumb || container,
buttons: buttons,
minPointers: pointers,
maxPointers: pointers,
relative: false,
enabled: _this.props.enabled,
disableUserSelect: _this.props.disableUserSelect,
});
// Set Events
_this._setEvents();
return _this;
}
/**
* Returns default static properties.
*/
Swipe.prototype._getStatic = function () {
return __assign(__assign({}, _super.prototype._getStatic.call(this)), props_1.STATIC_PROPS);
};
/**
* Returns default mutable properties.
*/
Swipe.prototype._getMutable = function () {
return __assign(__assign({}, _super.prototype._getMutable.call(this)), props_1.MUTABLE_PROPS);
};
Object.defineProperty(Swipe.prototype, "coords", {
/** Full coordinate snapshot (pointer space + `movement`). */
get: function () {
return this._coords.coords;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "container", {
/** Coordinate reference element. */
get: function () {
return this.props.container;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "hasInertia", {
/** Whether release inertia is running. */
get: function () {
return this._inertia.has;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "hasBounce", {
/** Whether overflow bounce-back timeline is running. */
get: function () {
return !!this._bounceTm;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "isSwiping", {
/** Whether a swipe gesture is in progress. */
get: function () {
return this._isSwiping;
},
enumerable: false,
configurable: true
});
/** Handles property updates */
Swipe.prototype._handleProps = function (props) {
_super.prototype._handleProps.call(this, props);
this._pointers.updateProps({ enabled: this.props.enabled });
this._styles.setInline();
if (!this.props.inertia || !this.props.enabled) {
this.cancelInertia();
}
if (!this.props.enabled) {
this.cancelBounce();
}
};
/** Sets event listeners */
Swipe.prototype._setEvents = function () {
var _this = this;
var callbacks = this.callbacks;
var container = this.props.container;
this._pointers.on('start', function () { return _this._handlePointersStart(); });
this._pointers.on('pointerdown', function (data) {
return callbacks.emit('pointerdown', data);
});
this._pointers.on('pointermove', function (data) {
return callbacks.emit('pointermove', data);
});
this._pointers.on('pointerup', function (data) { return callbacks.emit('pointerup', data); });
this._pointers.on('end', function () { return _this._handlePointersEnd(); });
var touchstart = (0, utils_1.addEventListener)(container, 'touchstart', function (event) { return _this._handleTouchStart(event); }, { passive: false });
this.onDestroy(function () { return touchstart(); });
};
/** Handles `touchstart` events */
Swipe.prototype._handleTouchStart = function (event) {
if (!this.props.enabled) {
return;
}
this._preventEdgeSwipe(event);
this.callbacks.emit('touchstart', event);
};
/** Prevents edge swipes if enabled */
Swipe.prototype._preventEdgeSwipe = function (event) {
var props = this.props;
if (!props.preventEdgeSwipe) {
return;
}
var threshold = props.edgeSwipeThreshold;
var x = event.targetTouches[0].pageX;
var shouldPrevent = x <= threshold || x >= (0, initVevet_1.initVevet)().width - threshold;
if (event.cancelable && shouldPrevent) {
event.preventDefault();
this.callbacks.emit('preventEdgeSwipe', undefined);
}
};
/** Handles pointers start */
Swipe.prototype._handlePointersStart = function () {
var _this = this;
this.cancelBounce();
this.cancelInertia();
var touchmove = (0, utils_1.addEventListener)(window, 'touchmove', this._handleTouchMove.bind(this), { passive: false });
var mousemove = (0, utils_1.addEventListener)(window, 'mousemove', this._handleMouseMove.bind(this));
var end = this._pointers.on('end', function () {
_this._handleEnd();
end();
touchmove();
mousemove();
});
this.onDestroy(function () {
end();
touchmove();
mousemove();
});
};
/** Handles pointers end */
Swipe.prototype._handlePointersEnd = function () {
if (!this._isSwiping) {
this.releaseBounce();
}
};
/** Handles `touchmove` event */
Swipe.prototype._handleTouchMove = function (event) {
this.callbacks.emit('touchmove', event);
if (this._isSwiping && this.props.preventTouchMove && event.cancelable) {
event.preventDefault();
}
this._handleMove('touch');
};
/** Handles `mousemove` event */
Swipe.prototype._handleMouseMove = function (event) {
if (this.props.requireCtrlKey && !event.ctrlKey) {
return;
}
this.callbacks.emit('mousemove', event);
this._handleMove('mouse');
};
/** Handles move events */
Swipe.prototype._handleMove = function (type) {
if (!this._pointers.move || !this.props.enabled) {
return;
}
var data = this._coords;
var state = data.decode(this._pointers.move.center);
if (this._isAborted) {
return;
}
// Save start coordinates
if (!this._startCoord) {
this._startCoord = __assign({}, state);
}
// Update start time
if (!this._startTime) {
this._startTime = +Date.now();
}
// check if can start
if (!this._isSwiping && !this._canStart(state, type)) {
return;
}
// start
if (!this._isSwiping) {
this.cancelInertia();
this.cancelBounce();
this._isSwiping = true;
this._startCoord = __assign({}, state);
data.setStart(state);
this.callbacks.emit('start', this.coords);
this._styles.append();
}
// move
this._move(state);
};
/** Checks if swipe can start */
Swipe.prototype._canStart = function (state, type) {
var _a = this, startCoord = _a._startCoord, startTime = _a._startTime;
if (!startCoord || !startTime) {
return false;
}
var _b = this.props, threshold = _b.threshold, minTime = _b.minTime, axis = _b.axis, willAbort = _b.willAbort;
var diff = {
x: state.x - startCoord.x,
y: state.y - startCoord.y,
};
// check threshold
var distX = diff.x;
var distY = diff.y;
var dist = Math.sqrt(Math.pow(distX, 2) + Math.pow(distY, 2));
if (dist < threshold) {
return false;
}
// check time
if (+new Date() - startTime < minTime) {
return false;
}
// check axis
if (axis) {
var rawAngle = (Math.atan2(Math.abs(diff.y), Math.abs(diff.x)) * 180) / Math.PI;
var normalizedAngle = axis === 'x' ? rawAngle : 90 - rawAngle;
if (normalizedAngle > 45) {
this._reset();
this._isAborted = true;
this.callbacks.emit('abort', undefined);
return false;
}
}
// Check if should abort
var shouldAbort = willAbort({
type: type,
state: state,
start: startCoord,
diff: diff,
});
if (shouldAbort) {
this._reset();
this._isAborted = true;
this.callbacks.emit('abort', undefined);
return false;
}
return true;
};
/** Handles move events */
Swipe.prototype._move = function (state, applyRatio) {
if (applyRatio === void 0) { applyRatio = true; }
var coords = this._coords;
// Update coords
coords.update(state, applyRatio);
// trigger callbacks
this.callbacks.emit('move', this.coords);
};
/** Handles swipe end */
Swipe.prototype._handleEnd = function () {
// reset
this._startTime = undefined;
this._isAborted = false;
// check swiping
if (!this.isSwiping) {
return;
}
// reset
this._reset();
// reset styles
this._styles.remove();
// calculate direction
var _a = this._coords.diff, diffX = _a.x, diffY = _a.y;
var absDiffX = Math.abs(diffX);
var absDiffY = Math.abs(diffY);
var directionThreshold = this.props.directionThreshold;
var endAxis = absDiffX > absDiffY ? 'x' : 'y';
if (endAxis === 'x' && absDiffX > directionThreshold) {
if (diffX > 0) {
this.callbacks.emit('toRight', undefined);
}
else if (diffX < 0) {
this.callbacks.emit('toLeft', undefined);
}
}
if (endAxis === 'y' && absDiffY > directionThreshold) {
if (diffY > 0) {
this.callbacks.emit('toBottom', undefined);
}
else if (diffY < 0) {
this.callbacks.emit('toTop', undefined);
}
}
// end callback
this.callbacks.emit('end', this.coords);
// end with inertia or bounce
var hasInertia = false;
if (this.props.inertia) {
hasInertia = this._releaseInertia();
}
if (!hasInertia) {
this.releaseBounce();
}
};
/** Reset swipe states */
Swipe.prototype._reset = function () {
this._startCoord = undefined;
this._isSwiping = false;
};
/** Apply inertia-based movement */
Swipe.prototype._releaseInertia = function () {
var _this = this;
return this._inertia.release(function (_a) {
var x = _a.x, y = _a.y, angle = _a.angle;
_this.callbacks.emit('inertia', undefined);
_this._move({ x: x, y: y, angle: angle, time: performance.now() }, false);
});
};
/** Apply bounce overflow animation */
Swipe.prototype.releaseBounce = function (targetDuration) {
var _this = this;
this.cancelBounce();
var exceeds = this._coords.exceeds;
var canBounce = this.props.canBounce();
if (!exceeds ||
(!exceeds.x && !exceeds.y && !exceeds.angle) ||
!canBounce) {
return;
}
var start = __assign({}, this.current);
var duration = targetDuration !== null && targetDuration !== void 0 ? targetDuration : this.props.bounceDuration;
var tm = new Timeline_1.Timeline({ duration: duration, easing: utils_1.EaseOutCubic });
this._bounceTm = tm;
this._coords.syncTempAngle();
tm.on('update', function (_a) {
var eased = _a.eased;
_this._move({
x: start.x - exceeds.x * eased,
y: start.y - exceeds.y * eased,
angle: start.angle - exceeds.angle * eased,
time: performance.now(),
}, false);
});
tm.on('end', this.cancelBounce.bind(this));
tm.play();
};
/** Cancel inertia */
Swipe.prototype.cancelInertia = function () {
this._inertia.cancel();
};
/** Cancel bounce */
Swipe.prototype.cancelBounce = function () {
var _a;
(_a = this._bounceTm) === null || _a === void 0 ? void 0 : _a.destroy();
this._bounceTm = undefined;
};
/** Calculate swipe bounds */
Swipe.prototype.calculateBounds = function () {
return this._coords.calculateBounds();
};
Object.defineProperty(Swipe.prototype, "start", {
/** Pointer position at swipe start. */
get: function () {
return this._coords.start;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "prev", {
/** Previous pointer position. */
get: function () {
return this._coords.prev;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "current", {
/** Current pointer position. */
get: function () {
return this._coords.current;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "diff", {
/** Offset from swipe start to current pointer position. */
get: function () {
return this._coords.diff;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "step", {
/** Offset from previous to current pointer position. */
get: function () {
return this._coords.step;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "accum", {
/** Absolute path length since swipe start. */
get: function () {
return this._coords.accum;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "movement", {
/** Total displacement in movement space (use for element transforms). */
get: function () {
return this._coords.movement;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Swipe.prototype, "scale", {
/** Current scale modifier. */
get: function () {
return this._coords.scale;
},
enumerable: false,
configurable: true
});
/**
* Sets programmatic scale in movement space.
* Optionally zooms toward an origin point and emits `move`.
*/
Swipe.prototype.setScale = function (value, origin) {
this._coords.applyScale(value, origin);
this._move(__assign(__assign({}, this.current), { time: performance.now() }));
if (!this._inertia.has) {
this.releaseBounce(0);
}
};
/**
* Sets programmatic displacement in movement space.
* Reapplies rubber, snap, emits `move`, and cancels overflow bounce.
*/
Swipe.prototype.setMovement = function (value) {
this._coords.movement = value;
this._move(__assign(__assign({}, this.current), { time: performance.now() }));
this.releaseBounce(0);
};
/**
* Destroys the component
*/
Swipe.prototype._destroy = function () {
_super.prototype._destroy.call(this);
this.cancelBounce();
this._pointers.destroy();
this._inertia.destroy();
this._styles.remove();
};
return Swipe;
}(base_1.Module));
exports.Swipe = Swipe;
//# sourceMappingURL=index.js.map