Skip to content

Instantly share code, notes, and snippets.

@mflux
Created September 25, 2014 05:55
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mflux/1de266f9596122e3d2c9 to your computer and use it in GitHub Desktop.
Save mflux/1de266f9596122e3d2c9 to your computer and use it in GitHub Desktop.
TWO.js text hack
/*
Goes after
Polygon.MakeObservable(Polygon.prototype);
//...
*/
var Text = Two.Text = function( text ) {
Two.Shape.call(this);
this._renderer.type = 'text';
this._text = text;
this._fontFamily = 'Arial';
this._fontSize = 12;
// Style properties
this.fill = '#fff';
this.stroke = '#000';
this.opacity = 1.0;
this.visible = true;
};
_.extend(Text, {
Properties: [
'fill',
'stroke',
'opacity',
'visible',
'text',
'fontFamily',
'fontSize',
],
MakeObservable: function(object) {
Two.Shape.MakeObservable(object);
// Only the first 4 properties are flagged like this. The subsequent
// properties behave differently and need to be hand written.
_.each(Text.Properties.slice(0, 4), function(property) {
var secret = '_' + property;
var flag = '_flag' + property.charAt(0).toUpperCase() + property.slice(1);
Object.defineProperty(object, property, {
get: function() {
return this[secret];
},
set: function(v) {
this[secret] = v;
this[flag] = true;
}
});
});
Object.defineProperty(object, 'text', {
get: function() {
if (this._flagText) {
this._updateText();
}
return this._length;
}
});
Object.defineProperty(object, 'fontFamily', {
get: function() {
if (this._flagFontFamily) {
this._updateText();
}
return this._length;
}
});
Object.defineProperty(object, 'fontSize', {
get: function() {
if (this._flagFontSize) {
this._updateText();
}
return this._length;
}
});
}
});
_.extend(Text.prototype, Two.Shape.prototype, {
// Flags
// http://en.wikipedia.org/wiki/Flag
_flagText: true,
_flagFontFamily: true,
_flagFontSize: true,
_flagFill: true,
_flagStroke: true,
_flagOpacity: true,
_flagVisible: true,
_fill: '#fff',
_stroke: '#000',
_opacity: 1.0,
_visible: true,
clone: function(parent) {
parent = parent || this.parent;
clone.translation.copy(this.translation);
clone.rotation = this.rotation;
clone.scale = this.scale;
parent.add(clone);
return clone;
},
toObject: function() {
var result = {
vertices: _.map(this.vertices, function(v) {
return v.toObject();
})
};
_.each(Two.Shape.Properties, function(k) {
result[k] = this[k];
}, this);
result.translation = this.translation.toObject;
result.rotation = this.rotation;
result.scale = this.scale;
return result;
},
noFill: function() {
this.fill = 'transparent';
return this;
},
noStroke: function() {
this.stroke = 'transparent';
return this;
},
/**
* Orient the vertices of the shape to the upper lefthand
* corner of the Text.
*/
corner: function() {
return this;
},
/**
* Orient the vertices of the shape to the center of the
* Text.
*/
center: function() {
return this;
},
/**
* Remove self from the scene / parent.
*/
remove: function() {
if (!this.parent) {
return this;
}
this.parent.remove(this);
return this;
},
/**
* Return an object with top, left, right, bottom, width, and height
* parameters of the group.
*/
getBoundingClientRect: function(shallow) {
// TODO: Update this to not __always__ update. Just when it needs to.
this._update(true);
return {
top: 0,
left: 0,
right: 0,
bottom: 0,
width: 0,
height: 0
};
},
_updateText: function(limit) {
//TODO: DRYness (function above)
this._update();
return this;
},
_update: function() {
Two.Shape.prototype._update.call(this);
return this;
},
flagReset: function() {
this._flagText = this._flagFontFamily = this._flagFontSize =
this._flagFill = this._flagStroke =
this._flagOpacity = this._flagVisible = false;
Two.Shape.prototype.flagReset.call(this);
return this;
}
});
Text.MakeObservable(Text.prototype);
/*
goes within
read: {
ellipse ...
...
text: function( node ){
// etc
}
*/
text: function( node ){
var fontFamily = node.getAttribute( 'font-family' );
var fontSize = node.getAttribute( 'font-size' );
var text = node.textContent;
var text = new Two.Text( text, fontFamily, fontSize );
return Two.Utils.applySvgAttributes(node, text);
},
/*
goes under
var canvas = {
group: { ...
},
// put here
text: ...
*/
text: {
render: function(ctx, forced, parentClipped) {
var matrix, stroke, fill, opacity, visible, text, fontFamily, fontSize,
closed, commands, length, last, next, prev, a, c, d, ux, uy, vx, vy,
ar, bl, br, cl, x, y, mask, clip;
// TODO: Add a check here to only invoke _update if need be.
this._update();
matrix = this._matrix.elements;
stroke = this._stroke;
fill = this._fill;
opacity = this._opacity * this.parent._renderer.opacity;
visible = this._visible;
text = this._text;
fontFamily = this._fontFamily;
fontSize = this._fontSize;
// Transform
ctx.save();
if (matrix) {
ctx.transform(
matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]);
}
// Styles
if (fill) {
ctx.fillStyle = fill;
}
if (stroke) {
ctx.strokeStyle = stroke;
}
if (_.isNumber(opacity)) {
ctx.globalAlpha = opacity;
}
ctx.font = fontSize +'px ' + fontFamily;
ctx.fillText( text, 0, 0 );
ctx.restore();
return this.flagReset();
}
},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment