UNPKG

@nativescript-community/additween

Version:
142 lines 4.94 kB
import { PlainObjectReducer } from "./PlainObjectReducer"; import { time } from '@nativescript/core/profiling'; if (!global.window) { global.window = { requestAnimationFrame, cancelAnimationFrame, performance: { now: time } }; } else if (!global.window.requestAnimationFrame) { global.window.requestAnimationFrame = requestAnimationFrame; global.window.cancelAnimationFrame = cancelAnimationFrame; if (!global.window.performance) { //@ts-ignore global.window.performance = { now: time }; } } function noop() { } function identity(t) { return t; } export class AdditiveTweening { constructor(options) { let frame = 0, lastTargetState = null, currentState = null, animationStack = []; const onRender = options.onRender || noop; const stateReducer = options.stateReducer || PlainObjectReducer; const onFinish = options.onFinish || noop; const onCancel = options.onCancel || noop; function filterOutdatedTargetsFromStack(time) { const filteredStack = []; for (let i = animationStack.length - 1; i >= 0; i--) { if (animationStack[i].end > time) { filteredStack.push(animationStack[i]); } } animationStack = filteredStack; } function getCurrentState(time) { let animation, remain; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion let target = stateReducer.clone(lastTargetState); for (let i = animationStack.length - 1; i >= 0; i--) { animation = animationStack[i]; if (animation.end < time) { continue; } remain = (animation.end - time) / animation.duration; target = stateReducer.reduce(target, animation.toState, animation.fromState, animation.easing(remain)); } return target; } function hasActiveAnimation(time) { for (let i = animationStack.length - 1; i >= 0; i--) { const animation = animationStack[i]; if (animation.end >= time) { return true; } } return false; } const animationStep = () => { if (lastTargetState === null) { return; } const time = this.now(); currentState = getCurrentState(time); onRender(currentState); if (hasActiveAnimation(time)) { frame = this.scheduleAnimationFrame(animationStep); } else { this.finish(); } }; /** * Returns true if any tweening is in process * @returns {boolean} */ this.isTweening = function () { return !!lastTargetState; }; /** * Immediately sets tweening to its final state. */ this.finish = function () { if (lastTargetState !== null) { onFinish(lastTargetState); lastTargetState = null; currentState = null; } }; /** * Cancels all active tweening processes */ this.cancel = function () { if (lastTargetState !== null) { if (window.cancelAnimationFrame) { window.cancelAnimationFrame(frame); frame = 0; } onCancel(); lastTargetState = null; currentState = null; } }; /** * Starts new tweening process * @param fromState * @param toState * @param {Number} duration Duration in ms * @param {Function} easing An easing function. It should take a number from [0,1] range and return a number from [0,1] range. */ this.tween = function (fromState, toState, duration, easing) { const time = this.now(); const animation = { duration: duration, end: time + duration, fromState: lastTargetState === null ? fromState : lastTargetState, toState: toState, easing: easing || identity, }; filterOutdatedTargetsFromStack(time); animationStack.push(animation); lastTargetState = toState; frame = this.scheduleAnimationFrame(animationStep); }; } scheduleAnimationFrame(cb) { return window.requestAnimationFrame(cb); } now() { if (!this._now) { this._now = window.performance?.now || Date.now; } return this._now(); } } //# sourceMappingURL=AdditiveTweening.js.map