animejs
Version:
JavaScript animation engine
167 lines (144 loc) • 4.46 kB
JavaScript
/**
* Anime.js - engine - ESM
* @version v4.3.6
* @license MIT
* @copyright 2026 - Julian Garnier
*/
import { globalVersions, defaults, globals } from '../core/globals.js';
import { isBrowser, doc, tickModes, K } from '../core/consts.js';
import { now, removeChild, forEachChildren } from '../core/helpers.js';
import { Clock } from '../core/clock.js';
import { tick } from '../core/render.js';
import { additive } from '../animation/additive.js';
/**
* @import {
* DefaultsParams,
* } from '../types/index.js'
*/
/**
* @import {
* Tickable,
* } from '../types/index.js'
*/
const engineTickMethod = /*#__PURE__*/ (() => isBrowser ? requestAnimationFrame : setImmediate)();
const engineCancelMethod = /*#__PURE__*/ (() => isBrowser ? cancelAnimationFrame : clearImmediate)();
class Engine extends Clock {
/** @param {Number} [initTime] */
constructor(initTime) {
super(initTime);
this.useDefaultMainLoop = true;
this.pauseOnDocumentHidden = true;
/** @type {DefaultsParams} */
this.defaults = defaults;
// this.paused = isBrowser && doc.hidden ? true : false;
this.paused = true;
/** @type {Number|NodeJS.Immediate} */
this.reqId = 0;
}
update() {
const time = this._currentTime = now();
if (this.requestTick(time)) {
this.computeDeltaTime(time);
const engineSpeed = this._speed;
const engineFps = this._fps;
let activeTickable = /** @type {Tickable} */(this._head);
while (activeTickable) {
const nextTickable = activeTickable._next;
if (!activeTickable.paused) {
tick(
activeTickable,
(time - activeTickable._startTime) * activeTickable._speed * engineSpeed,
0, // !muteCallbacks
0, // !internalRender
activeTickable._fps < engineFps ? activeTickable.requestTick(time) : tickModes.AUTO
);
} else {
removeChild(this, activeTickable);
this._hasChildren = !!this._tail;
activeTickable._running = false;
if (activeTickable.completed && !activeTickable._cancelled) {
activeTickable.cancel();
}
}
activeTickable = nextTickable;
}
additive.update();
}
}
wake() {
if (this.useDefaultMainLoop && !this.reqId) {
// Imediatly request a tick to update engine._lastTickTime and get accurate offsetPosition calculation in timer.js
this.requestTick(now());
this.reqId = engineTickMethod(tickEngine);
}
return this;
}
pause() {
if (!this.reqId) return;
this.paused = true;
return killEngine();
}
resume() {
if (!this.paused) return;
this.paused = false;
forEachChildren(this, (/** @type {Tickable} */child) => child.resetTime());
return this.wake();
}
// Getter and setter for speed
get speed() {
return this._speed * (globals.timeScale === 1 ? 1 : K);
}
set speed(playbackRate) {
this._speed = playbackRate * globals.timeScale;
forEachChildren(this, (/** @type {Tickable} */child) => child.speed = child._speed);
}
// Getter and setter for timeUnit
get timeUnit() {
return globals.timeScale === 1 ? 'ms' : 's';
}
set timeUnit(unit) {
const secondsScale = 0.001;
const isSecond = unit === 's';
const newScale = isSecond ? secondsScale : 1;
if (globals.timeScale !== newScale) {
globals.timeScale = newScale;
globals.tickThreshold = 200 * newScale;
const scaleFactor = isSecond ? secondsScale : K;
/** @type {Number} */
(this.defaults.duration) *= scaleFactor;
this._speed *= scaleFactor;
}
}
// Getter and setter for precision
get precision() {
return globals.precision;
}
set precision(precision) {
globals.precision = precision;
}
}
const engine = /*#__PURE__*/(() => {
const engine = new Engine(now());
if (isBrowser) {
globalVersions.engine = engine;
doc.addEventListener('visibilitychange', () => {
if (!engine.pauseOnDocumentHidden) return;
doc.hidden ? engine.pause() : engine.resume();
});
}
return engine;
})();
const tickEngine = () => {
if (engine._head) {
engine.reqId = engineTickMethod(tickEngine);
engine.update();
} else {
engine.reqId = 0;
}
};
const killEngine = () => {
engineCancelMethod(/** @type {NodeJS.Immediate & Number} */(engine.reqId));
engine.reqId = 0;
return engine;
};
export { engine };