@smoovy/tween
Version:
simple and easy-to-use tween lib
257 lines (256 loc) • 6.39 kB
JavaScript
"use strict";
import { Ticker } from "@smoovy/ticker";
export function mapRange(value, inStart, inEnd, outMin, outMax) {
return outMin + (outMax - outMin) / (inEnd - inStart) * (value - inStart);
}
export class TweenController {
constructor(config = {}) {
this.config = config;
this.ticker = Ticker.main;
this._overridden = false;
this._duration = 0;
this.listeners = /* @__PURE__ */ new Map();
this._resolved = false;
this._paused = false;
this._passed = 0;
this._progress = 0;
this._reversed = false;
this._started = false;
this.lastProgress = 0;
this.lastDelay = 0;
this._duration = typeof config.duration !== "undefined" ? config.duration : 500;
}
callback(fn, values = []) {
if (typeof fn === "function") {
fn(...values);
}
}
beforeStart() {
}
beforeStop() {
}
beforeSeek() {
}
onStart(cb) {
this.setListener("onStart", cb);
return this;
}
onComplete(cb) {
this.setListener("onComplete", cb);
return this;
}
onStop(cb) {
this.setListener("onStop", cb);
return this;
}
callListeners(name, args = []) {
const listeners = this.listeners.get(name);
if (listeners) {
listeners.forEach((cb) => cb(...args));
}
}
setListener(name, cb) {
if (!this.listeners.has(name)) {
this.listeners.set(name, []);
}
this.listeners.get(name).push(cb);
}
get progress() {
return this._progress;
}
get delay() {
return this.config.delay || 0;
}
get duration() {
return this._duration + this.delay;
}
get paused() {
return this._paused;
}
get complete() {
return this._resolved;
}
get passed() {
return this._progress * this.duration;
}
get reversed() {
return this._reversed;
}
get ticking() {
return !!this.task;
}
resolve() {
if (this.resolveFn && !this._resolved) {
this.resolveFn();
}
this._resolved = true;
}
override(overridden = true) {
this._overridden = overridden;
return this;
}
start(startFrom = 0, noReset = false) {
if (this._overridden) {
return this;
}
this._paused = false;
this.beforeStart();
if (!noReset) {
this.reset(0, true);
}
this.task = this.ticker.add((_delta, time) => {
if (!this._paused) {
this.seek(startFrom + time);
}
});
return this;
}
resume() {
return this.pause(false);
}
pause(paused = true) {
if (this._paused !== paused) {
this._paused = paused;
if (paused && this.task) {
this.task.kill();
delete this.task;
}
if (paused) {
this.callback(this.config.onPause);
} else {
this.callback(this.config.onResume);
}
if (!paused && !this.task) {
this.start(this._passed, true);
}
}
return this;
}
stop(noEvents = false) {
const ticking = this.ticking;
const started = this._started;
this._started = false;
if (ticking && this.task) {
this.task.kill();
delete this.task;
}
this.beforeStop();
if (!noEvents && started) {
this.callback(this.config.onStop, [ticking]);
this.callListeners("onStop", [ticking]);
}
return this;
}
hasStarted(ms) {
return !this._reversed && ms > 0 || this._reversed && ms < this.duration;
}
seekDelay(ms, noDelay = false, noEvents = false) {
const delay = noDelay ? 0 : this.delay;
if (delay > 0) {
if (!noDelay && ms <= delay) {
if (!noEvents) {
this.callback(this.config.onDelay, [ms, ms / delay]);
}
this.lastDelay = ms;
return false;
} else if (!noDelay && ms >= delay && this.lastDelay !== delay) {
if (!noEvents) {
this.callback(this.config.onDelay, [delay, 1]);
}
this.lastDelay = delay;
}
}
return true;
}
preSeek(ms, noEvents = false) {
if (this._resolved) {
return false;
}
if (!this._started && !noEvents && this.hasStarted(ms)) {
this._started = true;
this.callback(this.config.onStart);
this.callListeners("onStart");
this.beforeSeek();
}
return true;
}
seek(ms, noDelay = false, noEvents = false) {
if (!this.preSeek(ms, noEvents)) {
return this;
}
if (this._reversed) {
ms = this.duration - ms;
}
const delay = noDelay ? 0 : this.delay;
const passed = Math.min(Math.max(ms, 0), this.duration);
this._progress = Math.min(Math.max(ms / this.duration, 0), 1);
this._passed = passed;
if (!noEvents) {
this.callback(this.config.onSeek, [passed, this._progress]);
}
if (!noEvents && (!this._reversed && passed >= this.duration || this._reversed && passed <= 0)) {
this.process(this._reversed ? 0 : 1, this._reversed ? 0 : 1);
this.stop();
this.resolve();
this.callback(this.config.onComplete);
this.callListeners("onComplete");
return this;
}
if (!this.seekDelay(ms, noDelay, noEvents)) {
if (this.lastProgress !== 0) {
this.lastProgress = 0;
this.process(0, 0);
}
return this;
}
this.lastProgress = (passed - delay) / (this.duration - delay);
this.process(
this.config.easing ? this.config.easing(this.lastProgress) : this.lastProgress,
this.lastProgress
);
return this;
}
reset(seek = 0, noEvents = false) {
const lastActivity = this._reversed ? 1 - this._progress : this._progress;
this._resolved = false;
this.lastProgress = 0;
this.lastDelay = 0;
if (!noEvents) {
this.callback(this.config.onReset);
}
if (this._started || lastActivity > 0) {
this.seek(seek, true, noEvents);
}
if (this.task) {
this.stop(noEvents);
}
return this;
}
reverse() {
this._reversed = !this._reversed;
this.stop(true);
this.seek(0);
return this;
}
then(onFulfilled) {
const self = this;
const then = self.then;
self.then = void 0;
if (this._resolved) {
onFulfilled(this);
const result = Promise.resolve(this);
self.then = then;
return result;
}
return new Promise((resolve) => {
this.resolveFn = () => {
resolve(this);
onFulfilled(this);
self.then = then;
};
});
}
process(eased, linear) {
return this;
}
}