summaryrefslogtreecommitdiff
path: root/jaws/src/text.js
diff options
context:
space:
mode:
Diffstat (limited to 'jaws/src/text.js')
-rwxr-xr-xjaws/src/text.js619
1 files changed, 619 insertions, 0 deletions
diff --git a/jaws/src/text.js b/jaws/src/text.js
new file mode 100755
index 0000000..5856b75
--- /dev/null
+++ b/jaws/src/text.js
@@ -0,0 +1,619 @@
+/**
+ * @fileOverview A jaws.Text object with word-wrapping functionality.
+ * @class jaws.Text
+ * @property {integer} x Horizontal position (0 = furthest left)
+ * @property {integer} y Vertical position (0 = top)
+ * @property {number} alpha Transparency: 0 (fully transparent) to 1 (no transparency)
+ * @property {number} angle Angle in degrees (0-360)
+ * @property {string} anchor String stating how to anchor the sprite to canvas; @see Sprite#anchor
+ * @property {string} text The actual text to be displayed
+ * @property {string} fontFace A valid font-family
+ * @property {number} fontSize The size of the text in pixels
+ * @property {string} textAlign "start", "end", "left", "right", or "center"
+ * @property {string} textBaseline "top", "bottom", "hanging", "middle", "alphabetic", or "ideographic"
+ * @property {number} width The width of the rect() containing the text
+ * @property {number} height The height of the rect() containing the text
+ * @property {string} style The style to draw the text: "normal", "bold" or italic"
+ * @property {boolean} wordWrap If word-wrapping should be attempted
+ * @property {string} shadowColor The color of the shadow for the text
+ * @property {number} shadowBlur The amount of shadow blur (length away from text)
+ * @property {number} shadowOffsetX The start of the shadow from initial x
+ * @property {number} shadowOffsetY The start of the shadow from initial y
+ * @example
+ * var text = new Text({text: "Hello world!", x: 10, y: 10})
+ */
+
+var jaws = (function(jaws) {
+
+ /**
+ * jaws.Text constructor
+ * @constructor
+ * @param {object} options An object-literal collection of constructor values
+ */
+ jaws.Text = function(options) {
+ if (!(this instanceof arguments.callee))
+ return new arguments.callee(options);
+
+ this.set(options);
+
+ if (options.context) {
+ this.context = options.context;
+ }
+
+ if (!options.context) { // Defaults to jaws.context
+ if (jaws.context)
+ this.context = jaws.context;
+ }
+ };
+
+ /**
+ * The default values of jaws.Text properties
+ */
+ jaws.Text.prototype.default_options = {
+ x: 0,
+ y: 0,
+ alpha: 1,
+ angle: 0,
+ anchor_x: 0,
+ anchor_y: 0,
+ anchor: "top_left",
+ damping: 1,
+ style: "normal",
+ fontFace: "serif",
+ fontSize: 12,
+ color: "black",
+ textAlign: "start",
+ textBaseline: "alphabetic",
+ text: "",
+ wordWrap: false,
+ width: function(){ return jaws.width; },
+ height: function() { return jaws.height; },
+ shadowColor: null,
+ shadowBlur: null,
+ shadowOffsetX: null,
+ shadowOffsetY: null,
+ _constructor: null,
+ };
+
+ /**
+ * Overrides constructor values with defaults
+ * @this {jaws.Text}
+ * @param {object} options An object-literal collection of constructor values
+ * @returns {this}
+ * @see jaws.parseOptions
+ */
+ jaws.Text.prototype.set = function(options) {
+
+ jaws.parseOptions(this, options, this.default_options);
+
+ if (this.anchor)
+ this.setAnchor(this.anchor);
+
+ this.cacheOffsets();
+
+ return this;
+ };
+
+ /**
+ * Returns a new instance based on the current jaws.Text object
+ * @private
+ * @this {jaws.Text}
+ * @returns {object} The newly cloned object
+ */
+ jaws.Text.prototype.clone = function() {
+ var constructor = this._constructor ? eval(this._constructor) : this.constructor;
+ var new_sprite = new constructor(this.attributes());
+ new_sprite._constructor = this._constructor || this.constructor.name;
+ return new_sprite;
+ };
+
+ /**
+ * Rotate sprite by value degrees
+ * @this {jaws.Text}
+ * @param {number} value The amount of the rotation
+ * @returns {this} Current function scope
+ */
+ jaws.Text.prototype.rotate = function(value) {
+ this.angle += value;
+ return this;
+ };
+
+ /**
+ * Forces a rotation-angle on sprite
+ * @this {jaws.Text}
+ * @param {number} value The amount of the rotation
+ * @returns {this} Current function instance
+ */
+ jaws.Text.prototype.rotateTo = function(value) {
+ this.angle = value;
+ return this;
+ };
+
+ /**
+ * Move object to position x, y
+ * @this {jaws.Text}
+ * @param {number} x The x position to move to
+ * @param {number} y The y position to move to
+ * @returns {this} Current function instance
+ */
+ jaws.Text.prototype.moveTo = function(x, y) {
+ this.x = x;
+ this.y = y;
+ return this;
+ };
+
+ /**
+ * Modify x and/or y by a fixed amount
+ * @this {jaws.Text}
+ * @param {type} x The additional amount to move x
+ * @param {type} y The additional amount to move y
+ * @returns {this} Current function instance
+ */
+ jaws.Text.prototype.move = function(x, y) {
+ if (x)
+ this.x += x;
+ if (y)
+ this.y += y;
+ return this;
+ };
+
+ /**
+ * Sets x
+ * @param {number} value The new x value
+ * @returns {this} The current function instance
+ */
+ jaws.Text.prototype.setX = function(value) {
+ this.x = value;
+ return this;
+ };
+
+ /**
+ * Sets y
+ * @param {number} value The new y value
+ * @returns {this} The current function instance
+ */
+ jaws.Text.prototype.setY = function(value) {
+ this.y = value;
+ return this;
+ };
+
+ /**
+ * Position sprites top on the y-axis
+ * @param {number} value
+ * @returns {this} The current function instance
+ */
+ jaws.Text.prototype.setTop = function(value) {
+ this.y = value + this.top_offset;
+ return this;
+ };
+
+ /**
+ * Position sprites bottom on the y-axis
+ * @param {number} value
+ * @returns {this} The current function instance
+ */
+ jaws.Text.prototype.setBottom = function(value) {
+ this.y = value - this.bottom_offset;
+ return this;
+ };
+
+ /**
+ * Position sprites left side on the x-axis
+ * @param {number} value
+ * @returns {this} The current function instance
+ */
+ jaws.Text.prototype.setLeft = function(value) {
+ this.x = value + this.left_offset;
+ return this;
+ };
+
+ /**
+ * Position sprites right side on the x-axis
+ * @param {number} value
+ * @returns {this} The current function instance
+ */
+ jaws.Text.prototype.setRight = function(value) {
+ this.x = value - this.right_offset;
+ return this;
+ };
+
+ /**
+ * Set new width.
+ * @param {number} value The new width
+ * @returns {this}
+ */
+ jaws.Text.prototype.setWidth = function(value) {
+ this.width = value;
+ this.cacheOffsets();
+ return this;
+ };
+
+ /**
+ * Set new height.
+ * @param {number} value The new height
+ * @returns {this}
+ */
+ jaws.Text.prototype.setHeight = function(value) {
+ this.height = value;
+ this.cacheOffsets();
+ return this;
+ };
+
+ /**
+ * Resize sprite by adding width or height
+ * @param {number} width
+ * @param {number} height
+ * @returns {this}
+ */
+ jaws.Text.prototype.resize = function(width, height) {
+ this.width += width;
+ this.height += height;
+ this.cacheOffsets();
+ return this;
+ };
+
+ /**
+ * Resize sprite to exact width/height
+ * @this {jaws.Text}
+ * @param {number} width
+ * @param {number} height
+ * @returns {this}
+ */
+ jaws.Text.prototype.resizeTo = function(width, height) {
+ this.width = width;
+ this.height = height;
+ this.cacheOffsets();
+ return this;
+ };
+
+ /**
+ * The anchor could be describe as "the part of the text will be placed at x/y"
+ * or "when rotating, what point of the of the text will it rotate round"
+ * @param {string} value
+ * @returns {this} The current function instance
+ * @example
+ * For example, a topdown shooter could use setAnchor("center") --> Place middle of the ship on x/y
+ * .. and a sidescroller would probably use setAnchor("center_bottom") --> Place "feet" at x/y
+ */
+ jaws.Text.prototype.setAnchor = function(value) {
+ var anchors = {
+ top_left: [0, 0],
+ left_top: [0, 0],
+ center_left: [0, 0.5],
+ left_center: [0, 0.5],
+ bottom_left: [0, 1],
+ left_bottom: [0, 1],
+ top_center: [0.5, 0],
+ center_top: [0.5, 0],
+ center_center: [0.5, 0.5],
+ center: [0.5, 0.5],
+ bottom_center: [0.5, 1],
+ center_bottom: [0.5, 1],
+ top_right: [1, 0],
+ right_top: [1, 0],
+ center_right: [1, 0.5],
+ right_center: [1, 0.5],
+ bottom_right: [1, 1],
+ right_bottom: [1, 1]
+ };
+
+ if (anchors.hasOwnProperty(value)) {
+ this.anchor_x = anchors[value][0];
+ this.anchor_y = anchors[value][1];
+ this.cacheOffsets();
+ }
+ return this;
+ };
+
+ /**
+ * Save the object's dimensions
+ * @private
+ * @returns {this} The current function instance
+ */
+ jaws.Text.prototype.cacheOffsets = function() {
+
+ this.left_offset = this.width * this.anchor_x;
+ this.top_offset = this.height * this.anchor_y;
+ this.right_offset = this.width * (1.0 - this.anchor_x);
+ this.bottom_offset = this.height * (1.0 - this.anchor_y);
+
+ if (this.cached_rect)
+ this.cached_rect.resizeTo(this.width, this.height);
+ return this;
+ };
+
+ /**
+ * Returns a jaws.Rect() perfectly surrouning text.
+ * @returns {jaws.Rect}
+ */
+ jaws.Text.prototype.rect = function() {
+ if (!this.cached_rect && this.width)
+ this.cached_rect = new jaws.Rect(this.x, this.y, this.width, this.height);
+ if (this.cached_rect)
+ this.cached_rect.moveTo(this.x - this.left_offset, this.y - this.top_offset);
+ return this.cached_rect;
+ };
+
+ /**
+ * Draw sprite on active canvas or update its DOM-properties
+ * @this {jaws.Text}
+ * @returns {this} The current function instance
+ */
+ jaws.Text.prototype.draw = function() {
+ this.context.save();
+ if (this.angle !== 0) {
+ this.context.rotate(this.angle * Math.PI / 180);
+ }
+ this.context.globalAlpha = this.alpha;
+ this.context.translate(-this.left_offset, -this.top_offset); // Needs to be separate from above translate call cause of flipped
+ this.context.fillStyle = this.color;
+ this.context.font = this.style + " " + this.fontSize + "px " + this.fontFace;
+ this.context.textBaseline = this.textBaseline;
+ this.context.textAlign = this.textAlign;
+ if (this.shadowColor)
+ this.context.shadowColor = this.shadowColor;
+ if (this.shadowBlur)
+ this.context.shadowBlur = this.shadowBlur;
+ if (this.shadowOffsetX)
+ this.context.shadowOffsetX = this.shadowOffsetX;
+ if (this.shadowOffsetY)
+ this.context.shadowOffsetY = this.shadowOffsetY;
+ var oldY = this.y;
+ var oldX = this.x;
+ if (this.wordWrap)
+ {
+ var words = this.text.split(' ');
+ var nextLine = '';
+
+ for (var n = 0; n < words.length; n++)
+ {
+ var testLine = nextLine + words[n] + ' ';
+ var measurement = this.context.measureText(testLine);
+ if (this.y < oldY + this.height)
+ {
+ if (measurement.width > this.width)
+ {
+ this.context.fillText(nextLine, this.x, this.y);
+ nextLine = words[n] + ' ';
+ this.y += this.fontSize;
+ }
+ else {
+ nextLine = testLine;
+ }
+ this.context.fillText(nextLine, this.x, this.y);
+ }
+ }
+ }
+ else
+ {
+ if (this.context.measureText(this.text).width < this.width)
+ {
+ this.context.fillText(this.text, this.x, this.y);
+ }
+ else
+ {
+ var words = this.text.split(' ');
+ var nextLine = ' ';
+ for (var n = 0; n < words.length; n++)
+ {
+ var testLine = nextLine + words[n] + ' ';
+ if (this.context.measureText(testLine).width < Math.abs(this.width - this.x))
+ {
+ this.context.fillText(testLine, this.x, this.y);
+ nextLine = words[n] + ' ';
+ nextLine = testLine;
+ }
+ }
+ }
+ }
+ this.y = oldY;
+ this.x = oldX;
+ this.context.restore();
+ return this;
+ };
+
+ /**
+ * Returns sprite as a canvas context.
+ * (For certain browsers, a canvas context is faster to work with then a pure image.)
+ * @public
+ * @this {jaws.Text}
+ */
+ jaws.Text.prototype.asCanvasContext = function() {
+ var canvas = document.createElement("canvas");
+ canvas.width = this.width;
+ canvas.height = this.height;
+
+ var context = canvas.getContext("2d");
+ context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled;
+
+ this.context.fillStyle = this.color;
+ this.context.font = this.style + this.fontSize + "px " + this.fontFace;
+ this.context.textBaseline = this.textBaseline;
+ this.context.textAlign = this.textAlign;
+ if (this.shadowColor)
+ this.context.shadowColor = this.shadowColor;
+ if (this.shadowBlur)
+ this.context.shadowBlur = this.shadowBlur;
+ if (this.shadowOffsetX)
+ this.context.shadowOffsetX = this.shadowOffsetX;
+ if (this.shadowOffsetY)
+ this.context.shadowOffsetY = this.shadowOffsetY;
+ var oldY = this.y;
+ var oldX = this.x;
+ if (this.wordWrap)
+ {
+ var words = this.text.split(' ');
+ var nextLine = '';
+
+ for (var n = 0; n < words.length; n++)
+ {
+ var testLine = nextLine + words[n] + ' ';
+ var measurement = this.context.measureText(testLine);
+ if (this.y < oldY + this.height)
+ {
+ if (measurement.width > this.width)
+ {
+ this.context.fillText(nextLine, this.x, this.y);
+ nextLine = words[n] + ' ';
+ this.y += this.fontSize;
+ }
+ else {
+ nextLine = testLine;
+ }
+ this.context.fillText(nextLine, this.x, this.y);
+ }
+ }
+ }
+ else
+ {
+ if (this.context.measureText(this.text).width < this.width)
+ {
+ this.context.fillText(this.text, this.x, this.y);
+ }
+ else
+ {
+ var words = this.text.split(' ');
+ var nextLine = ' ';
+ for (var n = 0; n < words.length; n++)
+ {
+ var testLine = nextLine + words[n] + ' ';
+ if (this.context.measureText(testLine).width < Math.abs(this.width - this.x))
+ {
+ this.context.fillText(testLine, this.x, this.y);
+ nextLine = words[n] + ' ';
+ nextLine = testLine;
+ }
+ }
+ }
+ }
+ this.y = oldY;
+ this.x = oldX;
+ return context;
+ };
+
+ /**
+ * Returns text as a canvas
+ * @this {jaws.Text}
+ */
+ jaws.Text.prototype.asCanvas = function() {
+ var canvas = document.createElement("canvas");
+ canvas.width = this.width;
+ canvas.height = this.height;
+
+ var context = canvas.getContext("2d");
+ context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled;
+
+ this.context.fillStyle = this.color;
+ this.context.font = this.style + this.fontSize + "px " + this.fontFace;
+ this.context.textBaseline = this.textBaseline;
+ this.context.textAlign = this.textAlign;
+ if (this.shadowColor)
+ this.context.shadowColor = this.shadowColor;
+ if (this.shadowBlur)
+ this.context.shadowBlur = this.shadowBlur;
+ if (this.shadowOffsetX)
+ this.context.shadowOffsetX = this.shadowOffsetX;
+ if (this.shadowOffsetY)
+ this.context.shadowOffsetY = this.shadowOffsetY;
+ var oldY = this.y;
+ var oldX = this.x;
+ if (this.wordWrap)
+ {
+ var words = this.text.split(' ');
+ var nextLine = '';
+
+ for (var n = 0; n < words.length; n++)
+ {
+ var testLine = nextLine + words[n] + ' ';
+ var measurement = context.measureText(testLine);
+ if (this.y < oldY + this.height)
+ {
+ if (measurement.width > this.width)
+ {
+ context.fillText(nextLine, this.x, this.y);
+ nextLine = words[n] + ' ';
+ this.y += this.fontSize;
+ }
+ else {
+ nextLine = testLine;
+ }
+ context.fillText(nextLine, this.x, this.y);
+ }
+ }
+ }
+ else
+ {
+ if (context.measureText(this.text).width < this.width)
+ {
+ this.context.fillText(this.text, this.x, this.y);
+ }
+ else
+ {
+ var words = this.text.split(' ');
+ var nextLine = ' ';
+ for (var n = 0; n < words.length; n++)
+ {
+ var testLine = nextLine + words[n] + ' ';
+ if (context.measureText(testLine).width < Math.abs(this.width - this.x))
+ {
+ context.fillText(testLine, this.x, this.y);
+ nextLine = words[n] + ' ';
+ nextLine = testLine;
+ }
+ }
+ }
+ }
+ this.y = oldY;
+ this.x = oldX;
+ return canvas;
+ };
+
+ /**
+ * Returns Text's properties as a String
+ * @returns {string}
+ */
+ jaws.Text.prototype.toString = function() {
+ return "[Text " + this.x.toFixed(2) + ", " + this.y.toFixed(2) + ", " + this.width + ", " + this.height + "]";
+ };
+
+ /**
+ * Returns Text's properties as a pure object
+ * @returns {object}
+ */
+ jaws.Text.prototype.attributes = function() {
+ var object = this.options; // Start with all creation time properties
+ object["_constructor"] = this._constructor || "jaws.Text";
+ object["x"] = parseFloat(this.x.toFixed(2));
+ object["y"] = parseFloat(this.y.toFixed(2));
+ object["text"] = this.text;
+ object["alpha"] = this.alpha;
+ object["angle"] = parseFloat(this.angle.toFixed(2));
+ object["anchor_x"] = this.anchor_x;
+ object["anchor_y"] = this.anchor_y;
+ object["style"] = this.style;
+ object["fontSize"] = this.fontSize;
+ object["fontFace"] = this.fontFace;
+ object["color"] = this.color;
+ object["textAlign"] = this.textAlign;
+ object["textBaseline"] = this.textBaseline;
+ object["wordWrap"] = this.wordWrap;
+ object["width"] = this.width;
+ object["height"] = this.height;
+ return object;
+ };
+
+ /**
+ * Returns a JSON-string representing the properties of the Text.
+ * @returns {string}
+ */
+ jaws.Text.prototype.toJSON = function() {
+ return JSON.stringify(this.attributes());
+ };
+
+ return jaws;
+})(jaws || {});
+
+// Support CommonJS require()
+if (typeof module !== "undefined" && ('exports' in module)) {
+ module.exports = jaws.Text;
+}