(function() { 'use strict'; /** * Sprite constructor * @constructor * @memberof Konva * @augments Konva.Shape * @param {Object} config * @param {String} config.animation animation key * @param {Object} config.animations animation map * @param {Integer} [config.frameIndex] animation frame index * @param {Image} config.image image object * @@shapeParams * @@nodeParams * @example * var imageObj = new Image(); * imageObj.onload = function() { * var sprite = new Konva.Sprite({ * x: 200, * y: 100, * image: imageObj, * animation: 'standing', * animations: { * standing: [ * // x, y, width, height (6 frames) * 0, 0, 49, 109, * 52, 0, 49, 109, * 105, 0, 49, 109, * 158, 0, 49, 109, * 210, 0, 49, 109, * 262, 0, 49, 109 * ], * kicking: [ * // x, y, width, height (6 frames) * 0, 109, 45, 98, * 45, 109, 45, 98, * 95, 109, 63, 98, * 156, 109, 70, 98, * 229, 109, 60, 98, * 287, 109, 41, 98 * ] * }, * frameRate: 7, * frameIndex: 0 * }); * }; * imageObj.src = '/path/to/image.jpg' */ Konva.Sprite = function(config) { this.___init(config); }; Konva.Sprite.prototype = { ___init: function(config) { // call super constructor Konva.Shape.call(this, config); this.className = 'Sprite'; this._updated = true; var that = this; this.anim = new Konva.Animation(function() { // if we don't need to redraw layer we should return false var updated = that._updated; that._updated = false; return updated; }); this.on('animationChange.konva', function() { // reset index when animation changes this.frameIndex(0); }); this.on('frameIndexChange.konva', function() { this._updated = true; }); // smooth change for frameRate this.on('frameRateChange.konva', function() { if (!this.anim.isRunning()) { return; } clearInterval(this.interval); this._setInterval(); }); this.sceneFunc(this._sceneFunc); this.hitFunc(this._hitFunc); }, _sceneFunc: function(context) { var anim = this.getAnimation(), index = this.frameIndex(), ix4 = index * 4, set = this.getAnimations()[anim], offsets = this.frameOffsets(), x = set[ix4 + 0], y = set[ix4 + 1], width = set[ix4 + 2], height = set[ix4 + 3], image = this.getImage(); if (this.hasFill() || this.hasStroke()) { context.beginPath(); context.rect(0, 0, width, height); context.closePath(); context.fillStrokeShape(this); } if(image) { if (offsets) { var offset = offsets[anim], ix2 = index * 2; context.drawImage(image, x, y, width, height, offset[ix2 + 0], offset[ix2 + 1], width, height); } else { context.drawImage(image, x, y, width, height, 0, 0, width, height); } } }, _hitFunc: function(context) { var anim = this.getAnimation(), index = this.frameIndex(), ix4 = index * 4, set = this.getAnimations()[anim], offsets = this.frameOffsets(), width = set[ix4 + 2], height = set[ix4 + 3]; context.beginPath(); if (offsets) { var offset = offsets[anim]; var ix2 = index * 2; context.rect(offset[ix2 + 0], offset[ix2 + 1], width, height); } else { context.rect(0, 0, width, height); } context.closePath(); context.fillShape(this); }, _useBufferCanvas: function() { return (this.hasShadow() || this.getAbsoluteOpacity() !== 1) && this.hasStroke(); }, _setInterval: function() { var that = this; this.interval = setInterval(function() { that._updateIndex(); }, 1000 / this.getFrameRate()); }, /** * start sprite animation * @method * @memberof Konva.Sprite.prototype */ start: function() { var layer = this.getLayer(); /* * animation object has no executable function because * the updates are done with a fixed FPS with the setInterval * below. The anim object only needs the layer reference for * redraw */ this.anim.setLayers(layer); this._setInterval(); this.anim.start(); }, /** * stop sprite animation * @method * @memberof Konva.Sprite.prototype */ stop: function() { this.anim.stop(); clearInterval(this.interval); }, /** * determine if animation of sprite is running or not. returns true or false * @method * @memberof Konva.Animation.prototype * @returns {Boolean} */ isRunning: function() { return this.anim.isRunning(); }, _updateIndex: function() { var index = this.frameIndex(), animation = this.getAnimation(), animations = this.getAnimations(), anim = animations[animation], len = anim.length / 4; if(index < len - 1) { this.frameIndex(index + 1); } else { this.frameIndex(0); } } }; Konva.Util.extend(Konva.Sprite, Konva.Shape); // add getters setters Konva.Factory.addGetterSetter(Konva.Sprite, 'animation'); /** * get/set animation key * @name animation * @method * @memberof Konva.Sprite.prototype * @param {String} anim animation key * @returns {String} * @example * // get animation key * var animation = sprite.animation(); * * // set animation key * sprite.animation('kicking'); */ Konva.Factory.addGetterSetter(Konva.Sprite, 'animations'); /** * get/set animations map * @name animations * @method * @memberof Konva.Sprite.prototype * @param {Object} animations * @returns {Object} * @example * // get animations map * var animations = sprite.animations(); * * // set animations map * sprite.animations({ * standing: [ * // x, y, width, height (6 frames) * 0, 0, 49, 109, * 52, 0, 49, 109, * 105, 0, 49, 109, * 158, 0, 49, 109, * 210, 0, 49, 109, * 262, 0, 49, 109 * ], * kicking: [ * // x, y, width, height (6 frames) * 0, 109, 45, 98, * 45, 109, 45, 98, * 95, 109, 63, 98, * 156, 109, 70, 98, * 229, 109, 60, 98, * 287, 109, 41, 98 * ] * }); */ Konva.Factory.addGetterSetter(Konva.Sprite, 'frameOffsets'); /** * get/set offsets map * @name offsets * @method * @memberof Konva.Sprite.prototype * @param {Object} offsets * @returns {Object} * @example * // get offsets map * var offsets = sprite.offsets(); * * // set offsets map * sprite.offsets({ * standing: [ * // x, y (6 frames) * 0, 0, * 0, 0, * 5, 0, * 0, 0, * 0, 3, * 2, 0 * ], * kicking: [ * // x, y (6 frames) * 0, 5, * 5, 0, * 10, 0, * 0, 0, * 2, 1, * 0, 0 * ] * }); */ Konva.Factory.addGetterSetter(Konva.Sprite, 'image'); /** * get/set image * @name image * @method * @memberof Konva.Sprite.prototype * @param {Image} image * @returns {Image} * @example * // get image * var image = sprite.image(); * * // set image * sprite.image(imageObj); */ Konva.Factory.addGetterSetter(Konva.Sprite, 'frameIndex', 0); /** * set/set animation frame index * @name frameIndex * @method * @memberof Konva.Sprite.prototype * @param {Integer} frameIndex * @returns {Integer} * @example * // get animation frame index * var frameIndex = sprite.frameIndex(); * * // set animation frame index * sprite.frameIndex(3); */ Konva.Factory.addGetterSetter(Konva.Sprite, 'frameRate', 17); /** * get/set frame rate in frames per second. Increase this number to make the sprite * animation run faster, and decrease the number to make the sprite animation run slower * The default is 17 frames per second * @name frameRate * @method * @memberof Konva.Sprite.prototype * @param {Integer} frameRate * @returns {Integer} * @example * // get frame rate * var frameRate = sprite.frameRate(); * * // set frame rate to 2 frames per second * sprite.frameRate(2); */ Konva.Factory.backCompat(Konva.Sprite, { index: 'frameIndex', getIndex: 'getFrameIndex', setIndex: 'setFrameIndex' }); Konva.Collection.mapMethods(Konva.Sprite); })();