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.

137 lines 4.92 kB
import { Timeline } from '../../../components/Timeline'; import { isFiniteNumber } from '../../../internal/isFiniteNumber'; const VELOCITIES_COUNT = 4; export class SwipeInertia { constructor(_ctx) { this._ctx = _ctx; /** Velocity tracking */ this._velocities = []; } /** * Add new velocity sample */ addVelocity(velocity) { if (this.has) { return; } this._velocities.push(velocity); if (this._velocities.length > VELOCITIES_COUNT) { this._velocities.shift(); } } /** Update last timestamp */ updateLastTimestamp() { const velocities = this._velocities; const { length } = velocities; if (length > 0) { velocities[length - 1].timestamp = performance.now(); } } /** Returns current velocity */ get velocity() { const samples = this._velocities; if (samples.length < 2) { return { x: 0, y: 0, angle: 0 }; } let totalWeight = 0; let wvx = 0; let wvy = 0; let wva = 0; for (let i = 1; i < samples.length; i += 1) { const current = samples[i]; const previous = samples[i - 1]; const deltaX = current.x - previous.x; const deltaY = current.y - previous.y; let angleDiff = current.angle - previous.angle; if (angleDiff > 180) angleDiff -= 360; if (angleDiff < -180) angleDiff += 360; const deltatTime = Math.max(current.timestamp - previous.timestamp, 1); const sx = (deltaX / deltatTime) * 1000; const sy = (deltaY / deltatTime) * 1000; const sa = (angleDiff / deltatTime) * 1000; const weight = 1 / Math.exp(-deltatTime * 0.1); wvx += sx * weight; wvy += sy * weight; wva += sa * weight; totalWeight += weight; } if (totalWeight > 0) { return { x: wvx / totalWeight, y: wvy / totalWeight, angle: wva / totalWeight, }; } return { x: 0, y: 0, angle: 0 }; } /** Check if inertia is active */ get has() { return !!this._timeline; } /** Apply inertia-based movement */ release(onUpdate) { const swipe = this._ctx; const { props, callbacks } = swipe; const { inertiaRatio: ratio, velocityModifier } = props; const rawVelocity = this.velocity; const sourceVelocity = { x: rawVelocity.x * ratio, y: rawVelocity.y * ratio, angle: rawVelocity.angle * ratio, }; const finalVelocity = velocityModifier ? velocityModifier(sourceVelocity) : sourceVelocity; const { x: velocityX, y: velocityY, angle: velocityA } = finalVelocity; const distance = Math.sqrt(Math.pow(velocityX, 2) + Math.pow(velocityY, 2)); // Check if we have sufficient velocity if (distance < props.inertiaDistanceThreshold) { callbacks.emit('inertiaFail', undefined); return; } // Calculate animation duration const duration = props.inertiaDuration(distance); // Check if the animation duration is positive if (!isFiniteNumber(duration) || duration <= 0) { callbacks.emit('inertiaFail', undefined); return; } // Calculate the start and add matrices const addMatrix = { x: 0, y: 0, angle: 0 }; // Start the inertia animation this._timeline = new Timeline({ duration, easing: props.inertiaEasing }); this._timeline.on('start', () => callbacks.emit('inertiaStart', undefined)); this._timeline.on('update', ({ eased }) => { addMatrix.x = velocityX * eased; addMatrix.y = velocityY * eased; addMatrix.angle = velocityA * eased; onUpdate(addMatrix); callbacks.emit('inertia', undefined); }); this._timeline.on('end', () => { this.cancel(); callbacks.emit('inertiaEnd', undefined); }); setTimeout(() => { var _a; return (_a = this._timeline) === null || _a === void 0 ? void 0 : _a.play(); }, 0); } /** Destroy inertia animation */ cancel() { var _a; if (!this._timeline) { return; } if (this._timeline.progress < 1) { this._ctx.callbacks.emit('inertiaCancel', undefined); } (_a = this._timeline) === null || _a === void 0 ? void 0 : _a.destroy(); this._timeline = undefined; } /** Destroy instance */ destroy() { var _a; (_a = this._timeline) === null || _a === void 0 ? void 0 : _a.destroy(); } } //# sourceMappingURL=index.js.map