167 lines
5.5 KiB
JavaScript
167 lines
5.5 KiB
JavaScript
var _a;
|
|
import { glob } from "./Global.js";
|
|
import { Node } from "./Node.js";
|
|
import { Util } from "./Util.js";
|
|
const now = ((_a = glob.performance) === null || _a === void 0 ? void 0 : _a.now)
|
|
? () => glob.performance.now()
|
|
: Date.now;
|
|
/**
|
|
* Animation constructor.
|
|
* @constructor
|
|
* @memberof Konva
|
|
* @param {AnimationFn} func function executed on each animation frame. The function is passed a frame object, which contains
|
|
* timeDiff, lastTime, time, and frameRate properties. The timeDiff property is the number of milliseconds that have passed
|
|
* since the last animation frame. The time property accumulates elapsed animation time in milliseconds across stop/start calls, excluding paused time.
|
|
* The lastTime property is the current frame's clock timestamp in milliseconds. The frameRate property is the current frame rate in frames / second.
|
|
* Return false from function, if you don't need to redraw layer/layers on some frames.
|
|
* @param {Konva.Layer|Array} [layers] layer(s) to be redrawn on each animation frame. Can be a layer, an array of layers, or null.
|
|
* Not specifying a node will result in no redraw.
|
|
* @example
|
|
* // move a node to the right at 50 pixels / second
|
|
* var velocity = 50;
|
|
*
|
|
* var anim = new Konva.Animation(function(frame) {
|
|
* var dist = velocity * (frame.timeDiff / 1000);
|
|
* node.move({x: dist, y: 0});
|
|
* }, layer);
|
|
*
|
|
* anim.start();
|
|
*/
|
|
export class Animation {
|
|
constructor(func, layers) {
|
|
this.id = Animation.animIdCounter++;
|
|
this.frame = {
|
|
time: 0,
|
|
timeDiff: 0,
|
|
lastTime: now(),
|
|
frameRate: 0,
|
|
};
|
|
this.func = func;
|
|
this.setLayers(layers);
|
|
}
|
|
/**
|
|
* set layers to be redrawn on each animation frame
|
|
* @method
|
|
* @name Konva.Animation#setLayers
|
|
* @param {Konva.Layer|Array} [layers] layer(s) to be redrawn. Can be a layer, an array of layers, or null. Not specifying a node will result in no redraw.
|
|
* @return {Konva.Animation} this
|
|
*/
|
|
setLayers(layers) {
|
|
// a copy, so that addLayer() never pushes into the caller's array
|
|
this.layers = layers ? [].concat(layers) : [];
|
|
return this;
|
|
}
|
|
/**
|
|
* get layers
|
|
* @method
|
|
* @name Konva.Animation#getLayers
|
|
* @return {Array} Array of Konva.Layer
|
|
*/
|
|
getLayers() {
|
|
return this.layers;
|
|
}
|
|
/**
|
|
* add layer. Returns true if the layer was added, and false if it was not
|
|
* @method
|
|
* @name Konva.Animation#addLayer
|
|
* @param {Konva.Layer} layer to add
|
|
* @return {Bool} true if layer is added to animation, otherwise false
|
|
*/
|
|
addLayer(layer) {
|
|
const layers = this.layers;
|
|
const len = layers.length;
|
|
// don't add the layer if it already exists
|
|
for (let n = 0; n < len; n++) {
|
|
if (layers[n]._id === layer._id) {
|
|
return false;
|
|
}
|
|
}
|
|
this.layers.push(layer);
|
|
return true;
|
|
}
|
|
/**
|
|
* determine if animation is running or not. returns true or false
|
|
* @method
|
|
* @name Konva.Animation#isRunning
|
|
* @return {Bool} is animation running?
|
|
*/
|
|
isRunning() {
|
|
return Animation.animations.has(this);
|
|
}
|
|
/**
|
|
* start animation
|
|
* @method
|
|
* @name Konva.Animation#start
|
|
* @return {Konva.Animation} this
|
|
*/
|
|
start() {
|
|
this.stop();
|
|
this.frame.timeDiff = 0;
|
|
this.frame.lastTime = now();
|
|
Animation._addAnimation(this);
|
|
return this;
|
|
}
|
|
/**
|
|
* stop animation
|
|
* @method
|
|
* @name Konva.Animation#stop
|
|
* @return {Konva.Animation} this
|
|
*/
|
|
stop() {
|
|
Animation._removeAnimation(this);
|
|
return this;
|
|
}
|
|
_updateFrameObject(time) {
|
|
this.frame.timeDiff = time - this.frame.lastTime;
|
|
this.frame.lastTime = time;
|
|
this.frame.time += this.frame.timeDiff;
|
|
this.frame.frameRate = 1000 / this.frame.timeDiff;
|
|
}
|
|
static _addAnimation(anim) {
|
|
this.animations.add(anim);
|
|
this._handleAnimation();
|
|
}
|
|
static _removeAnimation(anim) {
|
|
this.animations.delete(anim);
|
|
}
|
|
static _runFrames() {
|
|
// every layer is drawn once, however many animations touch it
|
|
const layersToDraw = new Set();
|
|
// an animation may stop or restart itself, or another one, inside its
|
|
// function: each one runs at most once per frame, a stopped one not at all
|
|
Node._updateBatch(() => {
|
|
Array.from(this.animations).forEach((anim) => {
|
|
if (!this.animations.has(anim)) {
|
|
return;
|
|
}
|
|
anim._updateFrameObject(now());
|
|
// the function returns false to skip the redraw
|
|
if (anim.func && anim.func.call(anim, anim.frame) === false) {
|
|
return;
|
|
}
|
|
anim.layers.forEach((layer) => layer && layersToDraw.add(layer));
|
|
});
|
|
});
|
|
layersToDraw.forEach((layer) => layer.batchDraw());
|
|
}
|
|
static _animationLoop() {
|
|
const Anim = Animation;
|
|
if (Anim.animations.size) {
|
|
Anim._runFrames();
|
|
Util.requestAnimFrame(Anim._animationLoop);
|
|
}
|
|
else {
|
|
Anim.animRunning = false;
|
|
}
|
|
}
|
|
static _handleAnimation() {
|
|
if (!this.animRunning) {
|
|
this.animRunning = true;
|
|
Util.requestAnimFrame(this._animationLoop);
|
|
}
|
|
}
|
|
}
|
|
Animation.animations = new Set();
|
|
Animation.animIdCounter = 0;
|
|
Animation.animRunning = false;
|