diff options
Diffstat (limited to 'jaws/src/text.js')
| -rwxr-xr-x | jaws/src/text.js | 619 |
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; +} |
