@sky-foundry/two.js
Version:
A renderer agnostic two-dimensional drawing api for the web.
252 lines (180 loc) • 5.38 kB
JavaScript
(function(Two) {
var _ = Two.Utils;
var Path = Two.Path;
var Rectangle = Two.Rectangle;
var Sprite = Two.Sprite = function(path, ox, oy, cols, rows, frameRate) {
Path.call(this, [
new Two.Anchor(),
new Two.Anchor(),
new Two.Anchor(),
new Two.Anchor()
], true);
this.noStroke();
this.noFill();
if (path instanceof Two.Texture) {
this.texture = path;
} else if (_.isString(path)) {
this.texture = new Two.Texture(path);
}
this.origin = new Two.Vector();
this._update();
this.translation.set(ox || 0, oy || 0);
if (_.isNumber(cols)) {
this.columns = cols;
}
if (_.isNumber(rows)) {
this.rows = rows;
}
if (_.isNumber(frameRate)) {
this.frameRate = frameRate;
}
};
_.extend(Sprite, {
Properties: [
'texture', 'columns', 'rows', 'frameRate', 'index'
],
MakeObservable: function(obj) {
Rectangle.MakeObservable(obj);
_.each(Sprite.Properties, Two.Utils.defineProperty, obj);
}
})
_.extend(Sprite.prototype, Rectangle.prototype, {
_flagTexture: false,
_flagColumns: false,
_flagRows: false,
_flagFrameRate: false,
flagIndex: false,
// Private variables
_amount: 1,
_duration: 0,
_startTime: 0,
_playing: false,
_firstFrame: 0,
_lastFrame: 0,
_loop: true,
// Exposed through getter-setter
_texture: null,
_columns: 1,
_rows: 1,
_frameRate: 0,
_index: 0,
_origin: null,
constructor: Sprite,
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 Sprite(
this.texture, this.translation.x, this.translation.y,
this.columns, this.rows, this.frameRate
);
if (this.playing) {
clone.play(this._firstFrame, this._lastFrame);
clone._loop = this._loop;
}
if (parent) {
parent.add(clone);
}
return clone;
},
_update: function() {
var effect = this._texture;
var cols = this._columns;
var rows = this._rows;
var width, height, elapsed, amount, duration;
var index, iw, ih, isRange, frames;
if (this._flagColumns || this._flagRows) {
this._amount = this._columns * this._rows;
}
if (this._flagFrameRate) {
this._duration = 1000 * this._amount / this._frameRate;
}
if (this._flagTexture) {
this.fill = this._texture;
}
if (this._texture.loaded) {
iw = effect.image.width;
ih = effect.image.height;
width = iw / cols;
height = ih / rows;
amount = this._amount;
if (this.width !== width) {
this.width = width;
}
if (this.height !== height) {
this.height = height;
}
if (this._playing && this._frameRate > 0) {
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;
if (index >= this._lastFrame - 1 && this._onLastFrame) {
this._onLastFrame(); // Shortcut for chainable sprite animations
}
}
}
var col = this._index % cols;
var row = Math.floor(this._index / cols);
var ox = - width * col + (iw - width) / 2;
var oy = - height * row + (ih - height) / 2;
// TODO: Improve performance
if (ox !== effect.offset.x) {
effect.offset.x = ox;
}
if (oy !== effect.offset.y) {
effect.offset.y = oy;
}
}
Rectangle.prototype._update.call(this);
return this;
},
flagReset: function() {
this._flagTexture = this._flagColumns = this._flagRows
= this._flagFrameRate = false;
Rectangle.prototype.flagReset.call(this);
return this;
}
});
Sprite.MakeObservable(Sprite.prototype);
})((typeof global !== 'undefined' ? global : (this || window)).Two);