UNPKG

@sky-foundry/two.js

Version:

A renderer agnostic two-dimensional drawing api for the web.

316 lines (216 loc) 6.95 kB
(function(Two) { var _ = Two.Utils; var Path = Two.Path; var Rectangle = Two.Rectangle; var ImageSequence = Two.ImageSequence = function(paths, ox, oy, frameRate) { Path.call(this, [ new Two.Anchor(), new Two.Anchor(), new Two.Anchor(), new Two.Anchor() ], true); this._renderer.flagTextures = _.bind(ImageSequence.FlagTextures, this); this._renderer.bindTextures = _.bind(ImageSequence.BindTextures, this); this._renderer.unbindTextures = _.bind(ImageSequence.UnbindTextures, this); this.noStroke(); this.noFill(); this.textures = _.map(paths, ImageSequence.GenerateTexture, this); this.origin = new Two.Vector(); this._update(); this.translation.set(ox || 0, oy || 0); if (_.isNumber(frameRate)) { this.frameRate = frameRate; } else { this.frameRate = ImageSequence.DefaultFrameRate; } }; _.extend(ImageSequence, { Properties: [ 'frameRate', 'index' ], DefaultFrameRate: 30, FlagTextures: function() { this._flagTextures = true; }, BindTextures: function(items) { var i = items.length; while (i--) { items[i].bind(Two.Events.change, this._renderer.flagTextures); } this._renderer.flagTextures(); }, UnbindTextures: function(items) { var i = items.length; while (i--) { items[i].unbind(Two.Events.change, this._renderer.flagTextures); } this._renderer.flagTextures(); }, MakeObservable: function(obj) { Rectangle.MakeObservable(obj); _.each(ImageSequence.Properties, Two.Utils.defineProperty, obj); Object.defineProperty(obj, 'textures', { enumerable: true, get: function() { return this._textures; }, set: function(textures) { var updateTextures = this._renderer.flagTextures; var bindTextures = this._renderer.bindTextures; var unbindTextures = this._renderer.unbindTextures; // Remove previous listeners if (this._textures) { this._textures .unbind(Two.Events.insert, bindTextures) .unbind(Two.Events.remove, unbindTextures); } // Create new Collection with copy of vertices this._textures = new Two.Utils.Collection((textures || []).slice(0)); // Listen for Collection changes and bind / unbind this._textures .bind(Two.Events.insert, bindTextures) .bind(Two.Events.remove, unbindTextures); // Bind Initial Textures bindTextures(this._textures); } }); }, GenerateTexture: function(obj) { if (obj instanceof Two.Texture) { return obj; } else if (_.isString(obj)) { return new Two.Texture(obj); } } }); _.extend(ImageSequence.prototype, Rectangle.prototype, { _flagTextures: false, _flagFrameRate: false, _flagIndex: false, // Private variables _amount: 1, _duration: 0, _index: 0, _startTime: 0, _playing: false, _firstFrame: 0, _lastFrame: 0, _loop: true, // Exposed through getter-setter _textures: null, _frameRate: 0, _origin: null, constructor: ImageSequence, play: function(firstFrame, lastFrame, onLastFrame) { this._playing = true; this._firstFrame = 0; this._lastFrame = this.amount - 1; this._startTime = _.performance.now(); if (_.isNumber(firstFrame)) { this._firstFrame = firstFrame; } if (_.isNumber(lastFrame)) { this._lastFrame = lastFrame; } if (_.isFunction(onLastFrame)) { this._onLastFrame = onLastFrame; } else { delete this._onLastFrame; } if (this._index !== this._firstFrame) { this._startTime -= 1000 * Math.abs(this._index - this._firstFrame) / this._frameRate; } return this; }, pause: function() { this._playing = false; return this; }, stop: function() { this._playing = false; this._index = 0; return this; }, clone: function(parent) { var clone = new ImageSequence(this.textures, this.translation.x, this.translation.y, this.frameRate) clone._loop = this._loop; if (this._playing) { clone.play(); } if (parent) { parent.add(clone); } return clone; }, _update: function() { var effects = this._textures; var width, height, elapsed, amount, duration, texture; var index, frames; if (this._flagTextures) { this._amount = effects.length; } if (this._flagFrameRate) { this._duration = 1000 * this._amount / this._frameRate; } if (this._playing && this._frameRate > 0) { amount = this._amount; if (_.isNaN(this._lastFrame)) { this._lastFrame = amount - 1; } // TODO: Offload perf logic to instance of `Two`. elapsed = _.performance.now() - this._startTime; frames = this._lastFrame + 1; duration = 1000 * (frames - this._firstFrame) / this._frameRate; if (this._loop) { elapsed = elapsed % duration; } else { elapsed = Math.min(elapsed, duration); } index = _.lerp(this._firstFrame, frames, elapsed / duration); index = Math.floor(index); if (index !== this._index) { this._index = index; texture = effects[this._index]; if (texture.loaded) { width = texture.image.width; height = texture.image.height; if (this.width !== width) { this.width = width; } if (this.height !== height) { this.height = height; } this.fill = texture; if (index >= this._lastFrame - 1 && this._onLastFrame) { this._onLastFrame(); // Shortcut for chainable sprite animations } } } } else if (this._flagIndex || !(this.fill instanceof Two.Texture)) { texture = effects[this._index]; if (texture.loaded) { width = texture.image.width; height = texture.image.height; if (this.width !== width) { this.width = width; } if (this.height !== height) { this.height = height; } } this.fill = texture; } Rectangle.prototype._update.call(this); return this; }, flagReset: function() { this._flagTextures = this._flagFrameRate = false; Rectangle.prototype.flagReset.call(this); return this; } }); ImageSequence.MakeObservable(ImageSequence.prototype); })((typeof global !== 'undefined' ? global : (this || window)).Two);