animation-loop
Version:
Easily make and manage animation loops.
337 lines (281 loc) • 9.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.version = exports.default = exports.AnimationLoop = void 0;
var _from = _interopRequireDefault(require("@babel/runtime/core-js/array/from"));
var _set = _interopRequireDefault(require("@babel/runtime/core-js/set"));
var _Clock = _interopRequireDefault(require("./Clock"));
var _lowclass = _interopRequireDefault(require("lowclass"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var AnimationLoop = (0, _lowclass.default)('AnimationLoop', function (ref) {
var Public = ref.Public;
var Protected = ref.Protected;
var Private = ref.Private;
return {
constructor: function constructor() {
// `Private(this)` gives us access to truly "private" members
var self = Private(this);
self.animationFnsBefore = new _set.default();
self.animationFns = new _set.default();
self.animationFnsAfter = new _set.default();
self.baseFns = new _set.default();
self.animationFrame = null;
Protected(this).needsToRequestEachFrame = true;
self.elapsed = 0;
self.lastTick = 0;
self.interval = null;
self.intervals = -1;
self.clock = new _Clock.default();
self.started = false;
self.paused = false;
self.ticking = false;
self.forcedTick = false;
},
// read-only
get elapsed() {
return Private(this).elapsed;
},
get started() {
return Private(this).started;
},
get paused() {
return Private(this).paused;
},
get running() {
return Private(this).started && !Private(this).paused;
},
get ticking() {
return Private(this).ticking;
},
get interval() {
return Private(this).interval;
},
set interval(value) {
Private(this).interval = value;
},
addAnimationFnBefore: function addAnimationFnBefore(fn) {
var self = Private(this);
if (typeof fn === 'function') {
self.animationFnsBefore.add(fn);
}
if (this.running) {
self._startTicking();
}
return fn;
},
removeAnimationFnBefore: function removeAnimationFnBefore(fn) {
var self = Private(this);
self.animationFnsBefore.delete(fn);
},
addAnimationFn: function addAnimationFn(fn) {
var self = Private(this);
if (typeof fn === 'function') {
self.animationFns.add(fn);
}
if (this.running) {
self._startTicking();
}
return fn;
},
removeAnimationFn: function removeAnimationFn(fn) {
var self = Private(this);
self.animationFns.delete(fn);
},
addAnimationFnAfter: function addAnimationFnAfter(fn) {
var self = Private(this);
if (typeof fn === 'function') {
self.animationFnsAfter.add(fn);
}
if (this.running) {
self._startTicking();
}
return fn;
},
removeAnimationFnAfter: function removeAnimationFnAfter(fn) {
var self = Private(this);
self.animationFnsAfter.delete(fn);
},
hasAnimationFunctions: function hasAnimationFunctions() {
var self = Private(this);
return self.animationFnsBefore.size || self.animationFns.size || self.animationFnsAfter.size;
},
addBaseFn: function addBaseFn(fn) {
var self = Private(this);
if (typeof fn === 'function') {
self.baseFns.add(fn);
}
return fn;
},
removeBaseFn: function removeBaseFn(fn) {
var self = Private(this);
self.baseFns.delete(fn);
},
start: function start() {
var self = Private(this); // do nothing if already running
if (self.started && !self.paused) {
return;
}
self.started = true;
self.paused = false;
self.clock.start();
self._startTicking();
},
stop: function stop() {
var self = Private(this); // do nothing if already stopped
if (!self.started) {
return;
}
self.started = false;
self.elapsed = 0;
self.clock.stop();
self._stopTicking();
},
pause: function pause() {
var self = Private(this); // only pause if already running, otherwise do nothing
if (self.started && !self.paused) {
self.paused = true;
self.clock.stop();
self._stopTicking();
}
},
// add an empty function that removes itself on the next tick, forcing a
// tick of all other animation base functions.
forceTick: function forceTick() {
var self = Private(this);
if (self.forcedTick) {
return;
}
self.forcedTick = true;
this.addAnimationFn(function () {
return self.forcedTick = false;
});
},
addChildLoop: function addChildLoop() {
return new ChildAnimationLoop(this);
},
removeChildLoop: function removeChildLoop(child) {
Protected(child)._dispose();
},
protected: {
_requestFrame: function _requestFrame() {
var pro = Protected(this);
return requestAnimationFrame(function () {
return pro._tick();
});
},
_cancelFrame: function _cancelFrame(frame) {
cancelAnimationFrame(frame);
},
_dispose: function _dispose() {
Public(this).stop();
Private(this)._clearFunctions();
},
_tick: function _tick() {
var self = Private(this);
var dt = self.clock.getDelta();
self.elapsed += dt;
if (!self.interval) {
self._callAnimationFunctions(dt);
} else {
var numIntervals = Math.floor(self.elapsed / self.interval);
if (numIntervals > self.intervals) {
dt = self.elapsed - self.lastTick;
self.intervals = numIntervals;
self.lastTick = self.elapsed;
self._callAnimationFunctions(dt);
}
}
if (Protected(this).needsToRequestEachFrame) {
self.animationFrame = Protected(this)._requestFrame();
}
if (!Public(this).hasAnimationFunctions()) {
self._stopTicking();
}
}
},
private: {
_clearFunctions: function _clearFunctions() {
this.animationFnsBefore.clear();
this.animationFns.clear();
this.animationFnsAfter.clear();
this.baseFns.clear();
},
_startTicking: function _startTicking() {
if (!Public(this).hasAnimationFunctions()) {
return;
}
var self = Private(this);
if (self.ticking) {
return;
}
self.ticking = true;
self.animationFrame = Protected(this)._requestFrame();
},
_stopTicking: function _stopTicking() {
var self = Private(this);
self.ticking = false;
Protected(this)._cancelFrame(self.animationFrame);
},
_callAnimationFunctions: function _callAnimationFunctions(dt) {
var this$1 = this;
for (var i = 0, list = (0, _from.default)(this$1.animationFnsBefore); i < list.length; i += 1) {
var fn = list[i];
if (fn(dt, this$1.elapsed) === false) {
Public(this$1).removeAnimationFnBefore(fn);
}
}
for (var i$1 = 0, list$1 = (0, _from.default)(this$1.animationFns); i$1 < list$1.length; i$1 += 1) {
var fn$1 = list$1[i$1];
if (fn$1(dt, this$1.elapsed) === false) {
Public(this$1).removeAnimationFn(fn$1);
}
}
for (var i$2 = 0, list$2 = (0, _from.default)(this$1.animationFnsAfter); i$2 < list$2.length; i$2 += 1) {
var fn$2 = list$2[i$2];
if (fn$2(dt, this$1.elapsed) === false) {
Public(this$1).removeAnimationFnAfter(fn$2);
}
}
for (var i$3 = 0, list$3 = (0, _from.default)(this$1.baseFns); i$3 < list$3.length; i$3 += 1) {
var fn$3 = list$3[i$3];
if (fn$3(dt, this$1.elapsed) === false) {
Public(this$1).removeBaseFn(fn$3);
}
}
}
}
};
});
exports.AnimationLoop = AnimationLoop;
var _default = AnimationLoop; // Child loops hook into parent animationFns instead of requestAnimationFrame,
// so that there's a single rAF loop at the root AnimationLoop.
exports.default = _default;
var ChildAnimationLoop = (0, _lowclass.default)('ChildAnimationLoop').extends(AnimationLoop, function (ref) {
var Protected = ref.Protected;
var Super = ref.Super;
return {
constructor: function constructor(parentLoop) {
Super(this).constructor();
Protected(this).parentLoop = parentLoop;
Protected(this).needsToRequestEachFrame = false;
},
protected: {
_requestFrame: function _requestFrame() {
var pro = Protected(this);
return this.parentLoop.addAnimationFn(function () {
return pro._tick();
});
},
_cancelFrame: function _cancelFrame(frame) {
this.parentLoop.removeAnimationFn(frame);
},
_dispose: function _dispose() {
Super(this)._dispose();
this.parentLoop = null;
}
}
};
});
var version = '1.4.0';
exports.version = version;