diff --git a/src/core/index.js b/src/core/index.js index c5f3536..8b59b1e 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -25,7 +25,7 @@ // text Text: require('./text/Text'), - + TextStyle: require('./text/TextStyle'), // primitives Graphics: require('./graphics/Graphics'), GraphicsData: require('./graphics/GraphicsData'), diff --git a/src/core/index.js b/src/core/index.js index c5f3536..8b59b1e 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -25,7 +25,7 @@ // text Text: require('./text/Text'), - + TextStyle: require('./text/TextStyle'), // primitives Graphics: require('./graphics/Graphics'), GraphicsData: require('./graphics/GraphicsData'), diff --git a/src/core/text/Text.js b/src/core/text/Text.js index a726742..6a6d59b 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -1,8 +1,8 @@ var Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), math = require('../math'), - utils = require('../utils'), - CONST = require('../const'); + CONST = require('../const'), + TextStyle = require('./TextStyle'); /** * A Text Object will create a line or multiple lines of text. To split a line you can use '\n' in your text string, @@ -18,27 +18,8 @@ * @extends PIXI.Sprite * @memberof PIXI * @param text {string} The copy that you would like the text to display - * @param [style] {object} The style parameters - * @param [style.font] {string} default 'bold 20px Arial' The style and size of the font - * @param [style.fill='black'] {String|Number} A canvas fillstyle that will be used on the text e.g 'red', '#00FF00' - * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - * @param [style.stroke] {String|Number} A canvas fillstyle that will be used on the text stroke e.g 'blue', '#FCFF00' - * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used - * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true - * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses - * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text - * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00' - * @param [style.dropShadowAngle=Math.PI/4] {number} Set a angle of the drop shadow - * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow - * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius - * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will - * prevent this from happening by adding padding to the top and bottom of text height. - * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered. - * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve - * spiked text issues. Default is 'miter' (creates a sharp corner). - * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce - * or increase the spikiness of rendered text. + * @param [style] {object|TextStyle} The style parameters + * @param [resolution=CONST.RESOLUTION] The resolution of the canvas */ function Text(text, style, resolution) { @@ -76,6 +57,13 @@ * @private */ this._style = null; + /** + * Private listener to track style changes. + * + * @member {Function} + * @private + */ + this._styleListener = null; var texture = Texture.fromCanvas(this.canvas); texture.trim = new math.Rectangle(); @@ -142,29 +130,9 @@ }, /** - * Set the style of the text + * Set the style of the text. Set up an event listener to listen for changes on the style object and mark the text as dirty. * - * @param [style] {object} The style parameters - * @param [style.font='bold 20pt Arial'] {string} The style and size of the font - * @param [style.fill='black'] {string|number} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' - * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - * @param [style.stroke='black'] {string|number} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' - * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used - * @param [style.wordWrapWidth=100] {number} The width at which text will wrap - * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses - * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text - * @param [style.dropShadowColor='#000000'] {string|number} A fill style to be used on the dropshadow e.g 'red', '#00FF00' - * @param [style.dropShadowAngle=Math.PI/6] {number} Set a angle of the drop shadow - * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow - * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius - * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will - * prevent this from happening by adding padding to the top and bottom of text height. - * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered. - * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve - * spiked text issues. Default is 'miter' (creates a sharp corner). - * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce - * or increase the spikiness of rendered text. + * @param [style] {object|TextStyle} The style parameters * @memberof PIXI.Text# */ style: { @@ -175,42 +143,17 @@ set: function (style) { style = style || {}; - - if (typeof style.fill === 'number') { - style.fill = utils.hex2string(style.fill); + if (style instanceof TextStyle) + { + this._style = style; + } else + { + this._style = new TextStyle(style); } - - if (typeof style.stroke === 'number') { - style.stroke = utils.hex2string(style.stroke); - } - - if (typeof style.dropShadowColor === 'number') { - style.dropShadowColor = utils.hex2string(style.dropShadowColor); - } - - style.font = style.font || 'bold 20pt Arial'; - style.fill = style.fill || 'black'; - style.align = style.align || 'left'; - style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/pixijs/pixi.js/issues/136 - style.strokeThickness = style.strokeThickness || 0; - style.wordWrap = style.wordWrap || false; - style.wordWrapWidth = style.wordWrapWidth || 100; - - style.dropShadow = style.dropShadow || false; - style.dropShadowColor = style.dropShadowColor || '#000000'; - style.dropShadowAngle = style.dropShadowAngle !== undefined ? style.dropShadowAngle : Math.PI / 6; - style.dropShadowDistance = style.dropShadowDistance !== undefined ? style.dropShadowDistance : 5; - style.dropShadowBlur = style.dropShadowBlur !== undefined ? style.dropShadowBlur : 0; //shadowBlur is '0' by default according to HTML - - style.padding = style.padding || 0; - - style.textBaseline = style.textBaseline || 'alphabetic'; - - style.lineJoin = style.lineJoin || 'miter'; - style.miterLimit = style.miterLimit || 10; - - this._style = style; - this.dirty = true; + this._styleListener = this._style.on('styleChanged', function () + { + this.dirty = true; + }.bind(this)); } }, @@ -229,7 +172,7 @@ text = text || ' '; text = text.toString(); - + if (this._text === text) { return; @@ -295,9 +238,6 @@ } - //this.context.fillStyle="#FF0000"; - //this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); - this.context.font = style.font; this.context.strokeStyle = style.stroke; this.context.lineWidth = style.strokeThickness; @@ -431,8 +371,6 @@ { if (this.dirty) { - // this.resolution = 1//renderer.resolution; - this.updateText(); } @@ -616,7 +554,9 @@ this.context = null; this.canvas = null; + this._style.removeListener(this._styleListener); this._style = null; this._texture.destroy(destroyBaseTexture === undefined ? true : destroyBaseTexture); + }; diff --git a/src/core/index.js b/src/core/index.js index c5f3536..8b59b1e 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -25,7 +25,7 @@ // text Text: require('./text/Text'), - + TextStyle: require('./text/TextStyle'), // primitives Graphics: require('./graphics/Graphics'), GraphicsData: require('./graphics/GraphicsData'), diff --git a/src/core/text/Text.js b/src/core/text/Text.js index a726742..6a6d59b 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -1,8 +1,8 @@ var Sprite = require('../sprites/Sprite'), Texture = require('../textures/Texture'), math = require('../math'), - utils = require('../utils'), - CONST = require('../const'); + CONST = require('../const'), + TextStyle = require('./TextStyle'); /** * A Text Object will create a line or multiple lines of text. To split a line you can use '\n' in your text string, @@ -18,27 +18,8 @@ * @extends PIXI.Sprite * @memberof PIXI * @param text {string} The copy that you would like the text to display - * @param [style] {object} The style parameters - * @param [style.font] {string} default 'bold 20px Arial' The style and size of the font - * @param [style.fill='black'] {String|Number} A canvas fillstyle that will be used on the text e.g 'red', '#00FF00' - * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - * @param [style.stroke] {String|Number} A canvas fillstyle that will be used on the text stroke e.g 'blue', '#FCFF00' - * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used - * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true - * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses - * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text - * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00' - * @param [style.dropShadowAngle=Math.PI/4] {number} Set a angle of the drop shadow - * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow - * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius - * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will - * prevent this from happening by adding padding to the top and bottom of text height. - * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered. - * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve - * spiked text issues. Default is 'miter' (creates a sharp corner). - * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce - * or increase the spikiness of rendered text. + * @param [style] {object|TextStyle} The style parameters + * @param [resolution=CONST.RESOLUTION] The resolution of the canvas */ function Text(text, style, resolution) { @@ -76,6 +57,13 @@ * @private */ this._style = null; + /** + * Private listener to track style changes. + * + * @member {Function} + * @private + */ + this._styleListener = null; var texture = Texture.fromCanvas(this.canvas); texture.trim = new math.Rectangle(); @@ -142,29 +130,9 @@ }, /** - * Set the style of the text + * Set the style of the text. Set up an event listener to listen for changes on the style object and mark the text as dirty. * - * @param [style] {object} The style parameters - * @param [style.font='bold 20pt Arial'] {string} The style and size of the font - * @param [style.fill='black'] {string|number} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' - * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - * @param [style.stroke='black'] {string|number} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' - * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used - * @param [style.wordWrapWidth=100] {number} The width at which text will wrap - * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses - * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text - * @param [style.dropShadowColor='#000000'] {string|number} A fill style to be used on the dropshadow e.g 'red', '#00FF00' - * @param [style.dropShadowAngle=Math.PI/6] {number} Set a angle of the drop shadow - * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow - * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius - * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will - * prevent this from happening by adding padding to the top and bottom of text height. - * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered. - * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve - * spiked text issues. Default is 'miter' (creates a sharp corner). - * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce - * or increase the spikiness of rendered text. + * @param [style] {object|TextStyle} The style parameters * @memberof PIXI.Text# */ style: { @@ -175,42 +143,17 @@ set: function (style) { style = style || {}; - - if (typeof style.fill === 'number') { - style.fill = utils.hex2string(style.fill); + if (style instanceof TextStyle) + { + this._style = style; + } else + { + this._style = new TextStyle(style); } - - if (typeof style.stroke === 'number') { - style.stroke = utils.hex2string(style.stroke); - } - - if (typeof style.dropShadowColor === 'number') { - style.dropShadowColor = utils.hex2string(style.dropShadowColor); - } - - style.font = style.font || 'bold 20pt Arial'; - style.fill = style.fill || 'black'; - style.align = style.align || 'left'; - style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/pixijs/pixi.js/issues/136 - style.strokeThickness = style.strokeThickness || 0; - style.wordWrap = style.wordWrap || false; - style.wordWrapWidth = style.wordWrapWidth || 100; - - style.dropShadow = style.dropShadow || false; - style.dropShadowColor = style.dropShadowColor || '#000000'; - style.dropShadowAngle = style.dropShadowAngle !== undefined ? style.dropShadowAngle : Math.PI / 6; - style.dropShadowDistance = style.dropShadowDistance !== undefined ? style.dropShadowDistance : 5; - style.dropShadowBlur = style.dropShadowBlur !== undefined ? style.dropShadowBlur : 0; //shadowBlur is '0' by default according to HTML - - style.padding = style.padding || 0; - - style.textBaseline = style.textBaseline || 'alphabetic'; - - style.lineJoin = style.lineJoin || 'miter'; - style.miterLimit = style.miterLimit || 10; - - this._style = style; - this.dirty = true; + this._styleListener = this._style.on('styleChanged', function () + { + this.dirty = true; + }.bind(this)); } }, @@ -229,7 +172,7 @@ text = text || ' '; text = text.toString(); - + if (this._text === text) { return; @@ -295,9 +238,6 @@ } - //this.context.fillStyle="#FF0000"; - //this.context.fillRect(0, 0, this.canvas.width, this.canvas.height); - this.context.font = style.font; this.context.strokeStyle = style.stroke; this.context.lineWidth = style.strokeThickness; @@ -431,8 +371,6 @@ { if (this.dirty) { - // this.resolution = 1//renderer.resolution; - this.updateText(); } @@ -616,7 +554,9 @@ this.context = null; this.canvas = null; + this._style.removeListener(this._styleListener); this._style = null; this._texture.destroy(destroyBaseTexture === undefined ? true : destroyBaseTexture); + }; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js new file mode 100644 index 0000000..f05dd3a --- /dev/null +++ b/src/core/text/TextStyle.js @@ -0,0 +1,261 @@ +var EventEmitter = require('eventemitter3'), + utils = require('../utils'); + +/** + * A TextStyle Object decorates a Text Object. It acts as an event emitter, and can be shared between + * multiple Text objects. + * + * @class + * @extends EventEmitter + * @memberof PIXI + * @param [style] {object} The style parameters + * @param [style.font='bold 20pt Arial'] {string} The style and size of the font + * @param [style.fill='black'] {String|Number} A canvas fillstyle that will be used on the text e.g 'red', '#00FF00' + * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text + * @param [style.stroke='black'] {String|Number} A canvas fillstyle that will be used on the text stroke e.g 'blue', '#FCFF00' + * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke) + * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used + * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true + * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses + * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text + * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param [style.dropShadowAngle=Math.PI/4] {number} Set a angle of the drop shadow + * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow + * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius + * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will + * prevent this from happening by adding padding to the top and bottom of text height. + * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered. + * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve + * spiked text issues. Default is 'miter' (creates a sharp corner). + * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce + * or increase the spikiness of rendered text. + */ +function TextStyle(style) +{ + EventEmitter.call(this); + var properties = Object.assign({}, this._defaults, style); + for (var property in properties) + { + this[property] = properties[property]; + } +} + +TextStyle.prototype = Object.create(EventEmitter.prototype); +TextStyle.prototype.constructor = TextStyle; +module.exports = TextStyle; + +// Default settings. Explained in the constructor. +TextStyle.prototype._defaults = { + align: 'left', + dropShadow: false, + dropShadowAngle: Math.PI / 6, + dropShadowBlur: 0, + dropShadowColor: '#000000', + dropShadowDistance: 5, + lineHeight: null, + lineJoin: 'miter', + fill: 'black', + font: 'bold 20pt Arial', + miterLimit: 10, + padding: 0, + stroke: 'black', + strokeThickness: 0, + textBaseline: 'alphabetic', + wordWrap: false, + wordWrapWidth: 100 +}; + +/** + * Creates a new TextStyle object with the same values as this one. + * Note that the only the properties of the object are cloned, not its event emitter. + * + * @return {PIXI.TextStyle} + */ +TextStyle.prototype.clone = function () +{ + var clonedProperties = {}; + for (var key in this._defaults) + { + clonedProperties[key] = this[key]; + } + return new TextStyle(clonedProperties); +}; + +/** + * Create setters and getters for each of the style properties. Converts colors where necessary. + * Any set operation will emit a styleChanged event. + */ +Object.defineProperties(TextStyle.prototype, { + font: { + get: function () + { + return this._font; + }, set: function (font) + { + this._font = font; + this.emit('styleChanged'); + } + }, + fill: { + get: function () + { + return this._fill; + }, set: function (fill) + { + this._fill = typeof fill === 'number' ? utils.hex2string(fill) : fill; + this.emit('styleChanged'); + } + }, + align: { + get: function () + { + return this._align; + }, set: function (align) + { + this._align = align; + this.emit('styleChanged'); + } + }, + stroke: { + get: function () + { + return this._stroke; + }, set: function (stroke) + { + this._stroke = typeof stroke === 'number' ? utils.hex2string(stroke) : stroke; + this.emit('styleChanged'); + } + }, + strokeThickness: { + get: function () + { + return this._strokeThickness; + }, set: function (strokeThickness) + { + this._strokeThickness = strokeThickness; + this.emit('styleChanged'); + } + }, + wordWrap: { + get: function () + { + return this._wordWrap; + }, set: function (wordWrap) + { + this._wordWrap = wordWrap; + this.emit('styleChanged'); + } + }, + wordWrapWidth: { + get: function () + { + return this._wordWrapWidth; + }, set: function (wordWrapWidth) + { + this._wordWrapWidth = wordWrapWidth; + this.emit('styleChanged'); + } + }, + dropShadow: { + get: function () + { + return this._dropShadow; + }, set: function (dropShadow) + { + this._dropShadow = dropShadow; + this.emit('styleChanged'); + } + }, + dropShadowColor: { + get: function () + { + return this._dropShadowColor; + }, set: function (dropShadowColor) + { + this._dropShadowColor = typeof dropShadowColor === 'number' ? utils.hex2string(dropShadowColor) : dropShadowColor; + this.emit('styleChanged'); + } + }, + dropShadowAngle: { + get: function () + { + return this._dropShadowAngle; + }, set: function (dropShadowAngle) + { + this._dropShadowAngle = dropShadowAngle; + this.emit('styleChanged'); + } + }, + dropShadowDistance: { + get: function () + { + return this._dropShadowDistance; + }, set: function (dropShadowDistance) + { + this._dropShadowDistance = dropShadowDistance; + this.emit('styleChanged'); + } + }, + dropShadowBlur: { + get: function () + { + return this._dropShadowBlur; + }, set: function (dropShadowBlur) + { + this._dropShadowBlur = dropShadowBlur; + this.emit('styleChanged'); + } + }, + padding: { + get: function () + { + return this._padding; + }, set: function (padding) + { + this._padding = padding; + this.emit('styleChanged'); + } + }, + textBaseline: { + get: function () + { + return this._textBaseline; + }, set: function (textBaseline) + { + this._textBaseline = textBaseline; + this.emit('styleChanged'); + } + }, + lineJoin: { + get: function () + { + return this._lineJoin; + }, set: function (lineJoin) + { + this._lineJoin = lineJoin; + this.emit('styleChanged'); + } + }, + lineHeight: { + get: function () + { + return this._lineHeight; + }, set: function (lineHeight) + { + this._lineHeight = lineHeight; + this.emit('styleChanged'); + } + }, + miterLimit: { + get: function () + { + return this._miterLimit; + }, set: function (miterLimit) + { + this._miterLimit = miterLimit; + this.emit('styleChanged'); + } + } +}); + +