@sky-foundry/two.js
Version:
A renderer agnostic two-dimensional drawing api for the web.
344 lines (259 loc) • 7.94 kB
JavaScript
(function(Two) {
var root = Two.root;
var getComputedMatrix = Two.Utils.getComputedMatrix;
var _ = Two.Utils;
var canvas = getCanvas();
var ctx = canvas.getContext('2d');
/**
* @class
* @name Two.Text
* @param {String} message - The String to be rendered to the scene.
* @param {Number} [x=0] - The position in the x direction for the object.
* @param {Number} [y=0] - The position in the y direction for the object.
* @param {Object} [styles] - An object where styles are applied. Attribute must exist in Two.Text.Properties.
*/
var Text = Two.Text = function(message, x, y, styles) {
Two.Shape.call(this);
this._renderer.type = 'text';
this._renderer.flagFill = _.bind(Two.Text.FlagFill, this);
this._renderer.flagStroke = _.bind(Two.Text.FlagStroke, this);
this.value = message;
if (_.isNumber(x)) {
this.translation.x = x;
}
if (_.isNumber(y)) {
this.translation.y = y;
}
this.dashes = [];
if (!_.isObject(styles)) {
return this;
}
_.each(Two.Text.Properties, function(property) {
if (property in styles) {
this[property] = styles[property];
}
}, this);
};
_.extend(Two.Text, {
Ratio: 0.6,
Properties: [
'value', 'family', 'size', 'leading', 'alignment', 'linewidth', 'style',
'className', 'weight', 'decoration', 'baseline', 'opacity', 'visible',
'fill', 'stroke',
],
FlagFill: function() {
this._flagFill = true;
},
FlagStroke: function() {
this._flagStroke = true;
},
MakeObservable: function(object) {
Two.Shape.MakeObservable(object);
_.each(Two.Text.Properties.slice(0, 13), Two.Utils.defineProperty, object);
Object.defineProperty(object, 'fill', {
enumerable: true,
get: function() {
return this._fill;
},
set: function(f) {
if (this._fill instanceof Two.Gradient
|| this._fill instanceof Two.LinearGradient
|| this._fill instanceof Two.RadialGradient
|| this._fill instanceof Two.Texture) {
this._fill.unbind(Two.Events.change, this._renderer.flagFill);
}
this._fill = f;
this._flagFill = true;
if (this._fill instanceof Two.Gradient
|| this._fill instanceof Two.LinearGradient
|| this._fill instanceof Two.RadialGradient
|| this._fill instanceof Two.Texture) {
this._fill.bind(Two.Events.change, this._renderer.flagFill);
}
}
});
Object.defineProperty(object, 'stroke', {
enumerable: true,
get: function() {
return this._stroke;
},
set: function(f) {
if (this._stroke instanceof Two.Gradient
|| this._stroke instanceof Two.LinearGradient
|| this._stroke instanceof Two.RadialGradient
|| this._stroke instanceof Two.Texture) {
this._stroke.unbind(Two.Events.change, this._renderer.flagStroke);
}
this._stroke = f;
this._flagStroke = true;
if (this._stroke instanceof Two.Gradient
|| this._stroke instanceof Two.LinearGradient
|| this._stroke instanceof Two.RadialGradient
|| this._stroke instanceof Two.Texture) {
this._stroke.bind(Two.Events.change, this._renderer.flagStroke);
}
}
});
Object.defineProperty(object, 'clip', {
enumerable: true,
get: function() {
return this._clip;
},
set: function(v) {
this._clip = v;
this._flagClip = true;
}
});
}
});
_.extend(Two.Text.prototype, Two.Shape.prototype, {
// Flags
// http://en.wikipedia.org/wiki/Flag
_flagValue: true,
_flagFamily: true,
_flagSize: true,
_flagLeading: true,
_flagAlignment: true,
_flagBaseline: true,
_flagStyle: true,
_flagWeight: true,
_flagDecoration: true,
_flagFill: true,
_flagStroke: true,
_flagLinewidth: true,
_flagOpacity: true,
_flagClassName: true,
_flagVisible: true,
_flagClip: false,
// Underlying Properties
_value: '',
_family: 'sans-serif',
_size: 13,
_leading: 17,
_alignment: 'center',
_baseline: 'middle',
_style: 'normal',
_weight: 500,
_decoration: 'none',
_fill: '#000',
_stroke: 'transparent',
_linewidth: 1,
_opacity: 1,
_className: '',
_visible: true,
_clip: false,
constructor: Two.Text,
remove: function() {
if (!this.parent) {
return this;
}
this.parent.remove(this);
return this;
},
clone: function(parent) {
var clone = new Two.Text(this.value);
clone.translation.copy(this.translation);
clone.rotation = this.rotation;
clone.scale = this.scale;
_.each(Two.Text.Properties, function(property) {
clone[property] = this[property];
}, this);
if (parent) {
parent.add(clone);
}
return clone._update();
},
toObject: function() {
var result = {
translation: this.translation.toObject(),
rotation: this.rotation,
scale: this.scale
};
_.each(Two.Text.Properties, function(property) {
result[property] = this[property];
}, this);
return result;
},
noStroke: function() {
this.stroke = 'transparent';
return this;
},
noFill: function() {
this.fill = 'transparent';
return this;
},
// /**
// * A shim to not break `getBoundingClientRect` calls. TODO: Implement a
// * way to calculate proper bounding boxes of `Two.Text`.
// */
getBoundingClientRect: function(shallow) {
var matrix, border, l, x, y, i, v;
var left, right, top, bottom;
// TODO: Update this to not __always__ update. Just when it needs to.
this._update(true);
matrix = !!shallow ? this._matrix : getComputedMatrix(this);
var height = this.leading;
var width = this.value.length * this.size * Text.Ratio;
switch (this.alignment) {
case 'left':
left = 0;
right = width;
break;
case 'right':
left = - width;
right = 0;
break;
default:
left = - width / 2;
right = width / 2;
}
switch (this.baseline) {
case 'top':
top = 0;
bottom = height;
break;
case 'bottom':
top = - height;
bottom = 0;
break;
default:
top = - height / 2;
bottom = height / 2;
}
v = matrix.multiply(left, top, 1);
top = v.y;
left = v.x;
v = matrix.multiply(right, bottom, 1);
right = v.x;
bottom = v.y;
return {
top: top,
left: left,
right: right,
bottom: bottom,
width: right - left,
height: bottom - top
};
},
flagReset: function() {
this._flagValue = this._flagFamily = this._flagSize =
this._flagLeading = this._flagAlignment = this._flagFill =
this._flagStroke = this._flagLinewidth = this._flagOpacity =
this._flagVisible = this._flagClip = this._flagDecoration =
this._flagClassName = this._flagBaseline = false;
Two.Shape.prototype.flagReset.call(this);
return this;
}
});
Two.Text.MakeObservable(Two.Text.prototype);
function getCanvas() {
if (root.document) {
return root.document.createElement('canvas');
} else {
console.warn('Two.js: Unable to create canvas for Two.Text measurements.');
return {
getContext: _.identity
}
}
}
})((typeof global !== 'undefined' ? global : (this || window)).Two);