UNPKG

konva

Version:

HTML5 2d canvas library for interactive graphics, design editors, whiteboards, and diagrams.

164 lines (163 loc) 5.51 kB
var _a; import { glob } from "./Global.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 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;