@sky-foundry/two.js
Version:
A renderer agnostic two-dimensional drawing api for the web.
388 lines (290 loc) • 9.6 kB
JavaScript
(function(Two) {
var root = Two.root;
var _ = Two.Utils;
var anchor;
var regex = {
video: /\.(mp4|webm|ogg)$/i,
image: /\.(jpe?g|png|gif|tiff)$/i,
effect: /texture|gradient/i
};
if (root.document) {
anchor = document.createElement('a');
}
var Texture = Two.Texture = function(src, callback) {
this._renderer = {};
this._renderer.type = 'texture';
this._renderer.flagOffset = _.bind(Texture.FlagOffset, this);
this._renderer.flagScale = _.bind(Texture.FlagScale, this);
this.id = Two.Identifier + Two.uniqueId();
this.classList = [];
this.offset = new Two.Vector();
if (_.isFunction(callback)) {
var loaded = _.bind(function() {
this.unbind(Two.Events.load, loaded);
if (_.isFunction(callback)) {
callback();
}
}, this);
this.bind(Two.Events.load, loaded);
}
if (_.isString(src)) {
this.src = src;
} else if (_.isElement(src)) {
this.image = src;
}
this._update();
};
_.extend(Texture, {
Properties: [
'src',
'loaded',
'repeat'
],
RegularExpressions: regex,
ImageRegistry: new Two.Registry(),
getAbsoluteURL: function(path) {
if (!anchor) {
// TODO: Fix for headless environments
return path;
}
anchor.href = path;
return anchor.href;
},
loadHeadlessBuffer: new Function('texture', 'loaded', [
'var fs = require("fs");',
'var buffer = fs.readFileSync(texture.src);',
'texture.image.src = buffer;',
'loaded();'
].join('\n')),
getImage: function(src) {
var absoluteSrc = Texture.getAbsoluteURL(src);
if (Texture.ImageRegistry.contains(absoluteSrc)) {
return Texture.ImageRegistry.get(absoluteSrc);
}
var image;
if (Two.Utils.Image) {
// TODO: Fix for headless environments
image = new Two.Utils.Image();
Two.CanvasRenderer.Utils.shim(image, 'img');
} else if (root.document) {
if (regex.video.test(absoluteSrc)) {
image = document.createElement('video');
} else {
image = document.createElement('img');
}
} else {
console.warn('Two.js: no prototypical image defined for Two.Texture');
}
image.crossOrigin = 'anonymous';
return image;
},
Register: {
canvas: function(texture, callback) {
texture._src = '#' + texture.id;
Texture.ImageRegistry.add(texture.src, texture.image);
if (_.isFunction(callback)) {
callback();
}
},
img: function(texture, callback) {
var loaded = function(e) {
if (_.isFunction(texture.image.removeEventListener)) {
texture.image.removeEventListener('load', loaded, false);
texture.image.removeEventListener('error', error, false);
}
if (_.isFunction(callback)) {
callback();
}
};
var error = function(e) {
if (_.isFunction(texture.image.removeEventListener)) {
texture.image.removeEventListener('load', loaded, false);
texture.image.removeEventListener('error', error, false);
}
throw new Two.Utils.Error('unable to load ' + texture.src);
};
if (_.isNumber(texture.image.width) && texture.image.width > 0
&& _.isNumber(texture.image.height) && texture.image.height > 0) {
loaded();
} else if (_.isFunction(texture.image.addEventListener)) {
texture.image.addEventListener('load', loaded, false);
texture.image.addEventListener('error', error, false);
}
texture._src = Texture.getAbsoluteURL(texture._src);
if (texture.image && texture.image.getAttribute('two-src')) {
return;
}
texture.image.setAttribute('two-src', texture.src);
Texture.ImageRegistry.add(texture.src, texture.image);
if (Two.Utils.isHeadless) {
Texture.loadHeadlessBuffer(texture, loaded);
} else {
texture.image.src = texture.src;
}
},
video: function(texture, callback) {
var loaded = function(e) {
texture.image.removeEventListener('canplaythrough', loaded, false);
texture.image.removeEventListener('error', error, false);
texture.image.width = texture.image.videoWidth;
texture.image.height = texture.image.videoHeight;
texture.image.play();
if (_.isFunction(callback)) {
callback();
}
};
var error = function(e) {
texture.image.removeEventListener('canplaythrough', loaded, false);
texture.image.removeEventListener('error', error, false);
throw new Two.Utils.Error('unable to load ' + texture.src);
};
texture._src = Texture.getAbsoluteURL(texture._src);
texture.image.addEventListener('canplaythrough', loaded, false);
texture.image.addEventListener('error', error, false);
if (texture.image && texture.image.getAttribute('two-src')) {
return;
}
if (Two.Utils.isHeadless) {
throw new Two.Utils.Error('video textures are not implemented in headless environments.');
return;
}
texture.image.setAttribute('two-src', texture.src);
Texture.ImageRegistry.add(texture.src, texture.image);
texture.image.src = texture.src;
texture.image.loop = true;
texture.image.load();
}
},
load: function(texture, callback) {
var src = texture.src;
var image = texture.image;
var tag = image && image.nodeName.toLowerCase();
if (texture._flagImage) {
if (/canvas/i.test(tag)) {
Texture.Register.canvas(texture, callback);
} else {
texture._src = image.getAttribute('two-src') || image.src;
Texture.Register[tag](texture, callback);
}
}
if (texture._flagSrc) {
if (!image) {
texture.image = Texture.getImage(texture.src);
}
tag = texture.image.nodeName.toLowerCase();
Texture.Register[tag](texture, callback);
}
},
FlagOffset: function() {
this._flagOffset = true;
},
FlagScale: function() {
this._flagScale = true;
},
MakeObservable: function(object) {
_.each(Texture.Properties, Two.Utils.defineProperty, object);
Object.defineProperty(object, 'image', {
enumerable: true,
get: function() {
return this._image;
},
set: function(image) {
var tag = image && image.nodeName.toLowerCase();
var index;
switch (tag) {
case 'canvas':
index = '#' + image.id;
break;
default:
index = image.src;
}
if (Texture.ImageRegistry.contains(index)) {
this._image = Texture.ImageRegistry.get(image.src);
} else {
this._image = image;
}
this._flagImage = true;
}
});
Object.defineProperty(object, 'offset', {
enumerable: true,
get: function() {
return this._offset;
},
set: function(v) {
if (this._offset) {
this._offset.unbind(Two.Events.change, this._renderer.flagOffset);
}
this._offset = v;
this._offset.bind(Two.Events.change, this._renderer.flagOffset);
this._flagOffset = true;
}
});
Object.defineProperty(object, 'scale', {
enumerable: true,
get: function() {
return this._scale;
},
set: function(v) {
if (this._scale instanceof Two.Vector) {
this._scale.unbind(Two.Events.change, this._renderer.flagScale);
}
this._scale = v;
if (this._scale instanceof Two.Vector) {
this._scale.bind(Two.Events.change, this._renderer.flagScale);
}
this._flagScale = true;
}
});
}
});
_.extend(Texture.prototype, Two.Utils.Events, Two.Shape.prototype, {
_flagSrc: false,
_flagImage: false,
_flagVideo: false,
_flagLoaded: false,
_flagRepeat: false,
_flagOffset: false,
_flagScale: false,
_src: '',
_image: null,
_loaded: false,
_repeat: 'no-repeat',
_scale: 1,
_offset: null,
constructor: Texture,
clone: function() {
return new Texture(this.src);
},
toObject: function() {
return {
src: this.src,
image: this.image
}
},
_update: function() {
if (this._flagSrc || this._flagImage) {
this.trigger(Two.Events.change);
if (this._flagSrc || this._flagImage) {
this.loaded = false;
Texture.load(this, _.bind(function() {
this.loaded = true;
this
.trigger(Two.Events.change)
.trigger(Two.Events.load);
}, this));
}
}
if (this._image && this._image.readyState >= 4) {
this._flagVideo = true;
}
return this;
},
flagReset: function() {
this._flagSrc = this._flagImage = this._flagLoaded
= this._flagVideo = this._flagScale = this._flagOffset = false;
return this;
}
});
Texture.MakeObservable(Texture.prototype);
})((typeof global !== 'undefined' ? global : (this || window)).Two);