UNPKG

vevet

Version:

Vevet is a JavaScript library for creative development that simplifies crafting rich interactions like split text animations, carousels, marquees, preloading, and more.

254 lines 11.3 kB
"use strict"; 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 __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SwipeInertia = void 0; var Raf_1 = require("../../../components/Raf"); var isFiniteNumber_1 = require("../../../internal/isFiniteNumber"); var utils_1 = require("../../../utils"); var IDLE_VEC3 = { x: 0, y: 0, angle: 0 }; var IDLE_STATE = __assign(__assign({}, IDLE_VEC3), { time: 0 }); var LERP_APPROX = 0.01; var BELOW_THRESHOLD = 0.1; var SwipeInertia = /** @class */ (function () { function SwipeInertia(ctx) { this.ctx = ctx; this._velocity = __assign({}, IDLE_STATE); this._initialVelocity = __assign({}, IDLE_STATE); this._saveRawMovement = __assign({}, IDLE_VEC3); this._rawMovement = __assign({}, IDLE_VEC3); this._saveStep = __assign({}, IDLE_STATE); this._saveCurrent = __assign({}, IDLE_STATE); } Object.defineProperty(SwipeInertia.prototype, "has", { /** Check if inertia is active */ get: function () { return !!this._raf; }, enumerable: false, configurable: true }); /** Apply inertia-based movement */ SwipeInertia.prototype.release = function (onUpdate) { var ctx = this.ctx; var props = ctx.props; this._modifiedDistance = undefined; this._saveCurrent = __assign({}, ctx.coords.current); this._saveStep = __assign({}, ctx.coords.step); this._saveRawMovement = __assign({}, ctx.coords.rawMovement); this._rawMovement = __assign({}, ctx.coords.rawMovement); var data = this._calcVelocity(); if (!data || !(0, isFiniteNumber_1.isFiniteNumber)(data.dt) || data.dt <= 0) { ctx.onFail(); return false; } var linearSpeed = data.linearSpeed, angularSpeed = data.angularSpeed, vx = data.vx, vy = data.vy, va = data.va, threshold = data.threshold; if (!(0, isFiniteNumber_1.isFiniteNumber)(linearSpeed) || !(0, isFiniteNumber_1.isFiniteNumber)(angularSpeed) || (linearSpeed < threshold && angularSpeed < threshold)) { ctx.onFail(); return false; } this._onUpdate = onUpdate; this._velocity = { x: vx, y: vy, angle: va, time: performance.now() }; this._initialVelocity = __assign({}, this._velocity); if (props.inertiaDistanceModifier) { this._modifiedDistance = props.inertiaDistanceModifier({ x: this._predictDistance(vx, props.inertiaDecay), y: this._predictDistance(vy, props.inertiaDecay), angle: this._predictDistance(va, props.inertiaDecay), }); } this._raf = new Raf_1.Raf({ enabled: true, onFrame: this._handleRaf.bind(this), }); this.ctx.onStart(); return true; }; /** Calculate velocity */ SwipeInertia.prototype._calcVelocity = function () { var _a = this, current = _a._saveCurrent, step = _a._saveStep; var _b = this.ctx.props, inertiaRatio = _b.inertiaRatio, ratio = _b.ratio, maxVelocity = _b.maxVelocity, props = __rest(_b, ["inertiaRatio", "ratio", "maxVelocity"]); if (!current || !step) { return null; } var gap = performance.now() - current.time; var dt = Math.max(step.time, gap, 1); var iRatio = (0, isFiniteNumber_1.isFiniteNumber)(inertiaRatio) ? inertiaRatio : 1; var sRatio = (0, isFiniteNumber_1.isFiniteNumber)(ratio) ? ratio : 1; var finalRatio = sRatio * iRatio; var maxVX = maxVelocity.x ? Math.abs(maxVelocity.x) : 0; var vx = (step.x / dt) * finalRatio; vx = (0, utils_1.clamp)(vx, -maxVX, maxVX); var maxVY = maxVelocity.y ? Math.abs(maxVelocity.y) : 0; var vy = (step.y / dt) * finalRatio; vy = (0, utils_1.clamp)(vy, -maxVY, maxVY); var maxVA = maxVelocity.angle ? Math.abs(maxVelocity.angle) : 0; var va = (step.angle / dt) * finalRatio; va = (0, utils_1.clamp)(va, -maxVA, maxVA); var linearSpeed = Math.hypot(vx, vy) * 1000; var angularSpeed = Math.abs(va) * 1000; var threshold = props.inertiaThreshold; return { dt: dt, vx: vx, vy: vy, va: va, linearSpeed: linearSpeed, angularSpeed: angularSpeed, threshold: threshold }; }; /** Handle RAF update */ SwipeInertia.prototype._handleRaf = function () { var _a; if (!this._raf) { return; } var raf = this._raf; var duration = this._raf.duration; var _b = this.ctx, coords = _b.coords, props = _b.props; var _c = this, velocity = _c._velocity, startCurrent = _c._saveCurrent, startRawMovement = _c._saveRawMovement, rawMovement = _c._rawMovement, distance = _c._modifiedDistance, initial = _c._initialVelocity; var frameMs = duration; // Delta var dx = velocity.x * frameMs; var dy = velocity.y * frameMs; var dAngle = velocity.angle * frameMs; // Friction var frictionEase = raf.lerpFactor(props.inertiaDecay); velocity.x = (0, utils_1.lerp)(velocity.x, 0, frictionEase); velocity.y = (0, utils_1.lerp)(velocity.y, 0, frictionEase); velocity.angle = (0, utils_1.lerp)(velocity.angle, 0, frictionEase); // Movement if (distance) { var xP = this._getVelocityProgress(velocity.x, initial.x); var yP = this._getVelocityProgress(velocity.y, initial.y); var aP = this._getVelocityProgress(velocity.angle, initial.angle); rawMovement.x = startRawMovement.x + distance.x * xP; rawMovement.y = startRawMovement.y + distance.y * yP; rawMovement.angle = startRawMovement.angle + distance.angle * aP; } else { rawMovement.x += dx; rawMovement.y += dy; rawMovement.angle += dAngle; } // Bounce var isBouncing = false; var rawBounceEase = props.inertiaBounceEase; var bounceEase = rawBounceEase >= 1 ? 1 : raf.lerpFactor(rawBounceEase); // Bounce within bounds var bounds = coords.bounds; if (bounds === null || bounds === void 0 ? void 0 : bounds.x) { var bx = this._applyAxisBounce('x', rawMovement.x, velocity.x, bounds.x, bounceEase); rawMovement.x = bx.value; velocity.x = bx.velocity; isBouncing = 'bounceFinished' in bx ? true : isBouncing; } if (bounds === null || bounds === void 0 ? void 0 : bounds.y) { var by = this._applyAxisBounce('y', rawMovement.y, velocity.y, bounds.y, bounceEase); rawMovement.y = by.value; velocity.y = by.velocity; isBouncing = 'bounceFinished' in by ? true : isBouncing; } if (bounds === null || bounds === void 0 ? void 0 : bounds.angle) { var ba = this._applyAxisBounce('angle', rawMovement.angle, velocity.angle, bounds.angle, bounceEase); rawMovement.angle = ba.value; velocity.angle = ba.velocity; isBouncing = 'bounceFinished' in ba ? true : isBouncing; } // Callbacks var totalX = rawMovement.x - startRawMovement.x; var totalY = rawMovement.y - startRawMovement.y; var totalA = rawMovement.angle - startRawMovement.angle; var x = startCurrent.x + totalX; var y = startCurrent.y + totalY; var angle = startCurrent.angle + totalA; (_a = this._onUpdate) === null || _a === void 0 ? void 0 : _a.call(this, { x: x, y: y, angle: angle }); // Stop var linearStep = Math.hypot(dx, dy); var angularStep = Math.abs(dAngle); var shouldStop = linearStep < BELOW_THRESHOLD && angularStep < BELOW_THRESHOLD; if (distance) { shouldStop = Math.abs(totalX - distance.x) < LERP_APPROX && Math.abs(totalY - distance.y) < LERP_APPROX && Math.abs(totalA - distance.angle) < LERP_APPROX; } if (!isBouncing && shouldStop) { this.ctx.onEnd(); this._clear(); } }; /** Calculate velocity progress */ SwipeInertia.prototype._getVelocityProgress = function (v, initial) { if (Math.abs(initial) < BELOW_THRESHOLD) { return 1; } var p = 1 - Math.abs(v / initial); if (Math.abs(1 - p) < LERP_APPROX / 10) { return 1; } return p; }; SwipeInertia.prototype._predictDistance = function (velocity, decay, frameMs) { if (frameMs === void 0) { frameMs = 1000 / 60; } var k = (decay * 60) / 1000; var r = Math.exp(-k * frameMs); return (velocity * frameMs) / (1 - r); }; /** Apply exponential axis bounce overflow */ SwipeInertia.prototype._applyAxisBounce = function (axis, value, velocity, bounds, ease) { if (!bounds.length) { return { value: value, velocity: velocity }; } var snappy = this.ctx.coords.snap[axis]; var lo = typeof snappy === 'number' ? snappy : Math.min.apply(Math, bounds); var hi = typeof snappy === 'number' ? snappy : Math.max.apply(Math, bounds); if (value < lo || value > hi) { var target = (0, utils_1.clamp)(value, lo, hi); var val = (0, utils_1.lerp)(value, target, ease, LERP_APPROX); var vel = (0, utils_1.lerp)(velocity, 0, ease, LERP_APPROX); return { value: val, velocity: vel, bounceFinished: val === target && vel === 0, }; } return { value: value, velocity: velocity }; }; /** Clear data and stop animation */ SwipeInertia.prototype._clear = function () { var _a; (_a = this._raf) === null || _a === void 0 ? void 0 : _a.destroy(); this._raf = undefined; this._velocity = __assign({}, IDLE_STATE); }; /** Stop inertia animation */ SwipeInertia.prototype.cancel = function () { if (this._raf) { this.ctx.onCancel(); } this._clear(); }; /** Destroy instance */ SwipeInertia.prototype.destroy = function () { this._clear(); }; return SwipeInertia; }()); exports.SwipeInertia = SwipeInertia; //# sourceMappingURL=index.js.map