Skip to content

Commit a69c156

Browse files
committed
The Text Bounds is a rectangular region that allows you to align your text within it, regardless of the number of lines of text or position within the world. For example in an 800x600 sized game if you set the textBounds to be 0,0,800,600 and text alignment to 'left' and vertical alignment to 'bottom' then the text will render in the bottom-right hand corner of the game, regardless of the size of font you're using or the number of lines in the text itself.
Set the Style properties `boundsAlignH` and `boundsAlignV` or adjust them via the Text setters to change the alignment. It works by calculating the final position based on the Text.canvas size, which is modified as the text is updated. Some fonts have additional padding around them which you can mitigate by tweaking the Text.padding property. Setting a textBounds _doesn't_ update the wordWrapWidth, so be aware of the relationship between the two. Call this method with nothing defined for any of the parameters to reset an existing textBounds. phaserjs#1824
1 parent 0258245 commit a69c156

5 files changed

Lines changed: 213 additions & 266 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ Version 2.4 - "Katar" - in dev
317317
* When loading a BitmapText you can now specify either an XML file or a JSON file for the font data. This is useful in environments such as Cocoon where you don't have a native XML parser. If you wish to use JSON the formatting should be equal to the result of running a valid XML file through X2JS (thanks @Feenposhleen #1837)
318318
* Game Objects that have the Health component (such as Sprites) now have a new method: `heal` which adds the given amount to the health property, i.e. is the opposite of `damage` (thanks @stephandesouza #1794)
319319
* maxHealth is a new property that Game Objects with the Health component receive and works in combination with the `heal` method to ensure a health limit cap.
320+
* Text.setTextBounds is a rectangular region that allows you to align your text within it, regardless of the number of lines of text or position within the world. For example in an 800x600 sized game if you set the textBounds to be 0,0,800,600 and text alignment to 'left' and vertical alignment to 'bottom' then the text will render in the bottom-right hand corner of the game, regardless of the size of font you're using or the number of lines in the text itself.
320321

321322
### Updates
322323

src/gameobjects/Text.js

Lines changed: 195 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,29 @@
1919
* @param {number} x - X position of the new text object.
2020
* @param {number} y - Y position of the new text object.
2121
* @param {string} text - The actual text that will be written.
22-
* @param {object} style - The style object containing style attributes like font, font size, etc.
22+
* @param {object} [style] - The style properties to be set on the Text.
23+
* @param {string} [style.font='bold 20pt Arial'] - The style and size of the font.
24+
* @param {string} [style.fontStyle=(from font)] - The style of the font (eg. 'italic'): overrides the value in `style.font`.
25+
* @param {string} [style.fontVariant=(from font)] - The variant of the font (eg. 'small-caps'): overrides the value in `style.font`.
26+
* @param {string} [style.fontWeight=(from font)] - The weight of the font (eg. 'bold'): overrides the value in `style.font`.
27+
* @param {string|number} [style.fontSize=(from font)] - The size of the font (eg. 32 or '32px'): overrides the value in `style.font`.
28+
* @param {string} [style.backgroundColor=null] - A canvas fillstyle that will be used as the background for the whole Text object. Set to `null` to disable.
29+
* @param {string} [style.fill='black'] - A canvas fillstyle that will be used on the text eg 'red', '#00FF00'.
30+
* @param {string} [style.align='left'] - Horizontal alignment of each line in multiline text. Can be: 'left', 'center' or 'right'. Does not affect single lines of text (see `textBounds` and `boundsAlignH` for that).
31+
* @param {string} [style.boundsAlignH='left'] - Horizontal alignment of the text within the `textBounds`. Can be: 'left', 'center' or 'right'.
32+
* @param {string} [style.boundsAlignV='top'] - Vertical alignment of the text within the `textBounds`. Can be: 'top', 'middle' or 'bottom'.
33+
* @param {string} [style.stroke='black'] - A canvas stroke style that will be used on the text stroke eg 'blue', '#FCFF00'.
34+
* @param {number} [style.strokeThickness=0] - A number that represents the thickness of the stroke. Default is 0 (no stroke).
35+
* @param {boolean} [style.wordWrap=false] - Indicates if word wrap should be used.
36+
* @param {number} [style.wordWrapWidth=100] - The width in pixels at which text will wrap.
2337
*/
2438
Phaser.Text = function (game, x, y, text, style) {
2539

2640
x = x || 0;
2741
y = y || 0;
28-
text = text || ' ';
42+
text = text || '';
2943
style = style || {};
3044

31-
if (text.length === 0)
32-
{
33-
text = ' ';
34-
}
35-
else
36-
{
37-
text = text.toString();
38-
}
39-
4045
/**
4146
* @property {number} type - The const type of this object.
4247
* @default
@@ -56,6 +61,14 @@ Phaser.Text = function (game, x, y, text, style) {
5661
*/
5762
this.padding = new Phaser.Point();
5863

64+
/**
65+
* The textBounds property allows you to specify a rectangular region upon which text alignment is based.
66+
* See `Text.setTextBounds` for more details.
67+
* @property {Phaser.Rectangle} textBounds
68+
* @readOnly
69+
*/
70+
this.textBounds = null;
71+
5972
/**
6073
* @property {HTMLCanvasElement} canvas - The canvas element that the text is rendered.
6174
*/
@@ -116,9 +129,11 @@ Phaser.Text = function (game, x, y, text, style) {
116129

117130
Phaser.Sprite.call(this, game, x, y, PIXI.Texture.fromCanvas(this.canvas));
118131

132+
this.texture.trim = new Phaser.Rectangle();
133+
119134
this.setStyle(style);
120135

121-
if (text !== ' ')
136+
if (text !== '')
122137
{
123138
this.updateText();
124139
}
@@ -192,6 +207,7 @@ Phaser.Text.prototype.destroy = function (destroyChildren) {
192207
* @param {number} [blur=0] - The shadowBlur value. Make the shadow softer by applying a Gaussian blur to it. A number from 0 (no blur) up to approx. 10 (depending on scene).
193208
* @param {boolean} [shadowStroke=true] - Apply the drop shadow to the Text stroke (if set).
194209
* @param {boolean} [shadowFill=true] - Apply the drop shadow to the Text fill (if set).
210+
* @return {Phaser.Text} This Text instance.
195211
*/
196212
Phaser.Text.prototype.setShadow = function (x, y, color, blur, shadowStroke, shadowFill) {
197213

@@ -210,6 +226,8 @@ Phaser.Text.prototype.setShadow = function (x, y, color, blur, shadowStroke, sha
210226
this.style.shadowFill = shadowFill;
211227
this.dirty = true;
212228

229+
return this;
230+
213231
};
214232

215233
/**
@@ -224,11 +242,14 @@ Phaser.Text.prototype.setShadow = function (x, y, color, blur, shadowStroke, sha
224242
* @param {string|number} [style.fontSize=(from font)] - The size of the font (eg. 32 or '32px'): overrides the value in `style.font`.
225243
* @param {string} [style.backgroundColor=null] - A canvas fillstyle that will be used as the background for the whole Text object. Set to `null` to disable.
226244
* @param {string} [style.fill='black'] - A canvas fillstyle that will be used on the text eg 'red', '#00FF00'.
227-
* @param {string} [style.align='left'] - Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text.
245+
* @param {string} [style.align='left'] - Horizontal alignment of each line in multiline text. Can be: 'left', 'center' or 'right'. Does not affect single lines of text (see `textBounds` and `boundsAlignH` for that).
246+
* @param {string} [style.boundsAlignH='left'] - Horizontal alignment of the text within the `textBounds`. Can be: 'left', 'center' or 'right'.
247+
* @param {string} [style.boundsAlignV='top'] - Vertical alignment of the text within the `textBounds`. Can be: 'top', 'middle' or 'bottom'.
228248
* @param {string} [style.stroke='black'] - A canvas stroke style that will be used on the text stroke eg 'blue', '#FCFF00'.
229249
* @param {number} [style.strokeThickness=0] - A number that represents the thickness of the stroke. Default is 0 (no stroke).
230250
* @param {boolean} [style.wordWrap=false] - Indicates if word wrap should be used.
231251
* @param {number} [style.wordWrapWidth=100] - The width in pixels at which text will wrap.
252+
* @return {Phaser.Text} This Text instance.
232253
*/
233254
Phaser.Text.prototype.setStyle = function (style) {
234255

@@ -237,6 +258,8 @@ Phaser.Text.prototype.setStyle = function (style) {
237258
style.backgroundColor = style.backgroundColor || null;
238259
style.fill = style.fill || 'black';
239260
style.align = style.align || 'left';
261+
style.boundsAlignH = style.boundsAlignH || 'left';
262+
style.boundsAlignV = style.boundsAlignV || 'top';
240263
style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/GoodBoyDigital/pixi.js/issues/136
241264
style.strokeThickness = style.strokeThickness || 0;
242265
style.wordWrap = style.wordWrap || false;
@@ -279,6 +302,8 @@ Phaser.Text.prototype.setStyle = function (style) {
279302
this.style = style;
280303
this.dirty = true;
281304

305+
return this;
306+
282307
};
283308

284309
/**
@@ -476,12 +501,15 @@ Phaser.Text.prototype.updateLine = function (line, x, y) {
476501
* Clears any previously set color stops.
477502
*
478503
* @method Phaser.Text#clearColors
504+
* @return {Phaser.Text} This Text instance.
479505
*/
480506
Phaser.Text.prototype.clearColors = function () {
481507

482508
this.colors = [];
483509
this.dirty = true;
484510

511+
return this;
512+
485513
};
486514

487515
/**
@@ -495,12 +523,15 @@ Phaser.Text.prototype.clearColors = function () {
495523
* @method Phaser.Text#addColor
496524
* @param {string} color - A canvas fillstyle that will be used on the text eg `red`, `#00FF00`, `rgba()`.
497525
* @param {number} position - The index of the character in the string to start applying this color value from.
526+
* @return {Phaser.Text} This Text instance.
498527
*/
499528
Phaser.Text.prototype.addColor = function (color, position) {
500529

501530
this.colors[position] = color;
502531
this.dirty = true;
503532

533+
return this;
534+
504535
};
505536

506537
/**
@@ -660,12 +691,61 @@ Phaser.Text.prototype.componentsToFont = function (components) {
660691
*
661692
* @method Phaser.Text#setText
662693
* @param {string} [text] - The text to be displayed. Set to an empty string to clear text that is already present.
694+
* @return {Phaser.Text} This Text instance.
663695
*/
664696
Phaser.Text.prototype.setText = function (text) {
665697

666698
this.text = text.toString() || '';
667699
this.dirty = true;
668700

701+
return this;
702+
703+
};
704+
705+
/**
706+
* The Text Bounds is a rectangular region that allows you to align your text within it, regardless of the number of lines of text
707+
* or position within the world. For example in an 800x600 sized game if you set the textBounds to be 0,0,800,600 and text alignment
708+
* to 'left' and vertical alignment to 'bottom' then the text will render in the bottom-right hand corner of the game, regardless of
709+
* the size of font you're using or the number of lines in the text itself.
710+
*
711+
* Set the Style properties `boundsAlignH` and `boundsAlignV` or adjust them via the Text setters to change the alignment.
712+
*
713+
* It works by calculating the final position based on the Text.canvas size, which is modified as the text is updated. Some fonts
714+
* have additional padding around them which you can mitigate by tweaking the Text.padding property.
715+
*
716+
* Setting a textBounds _doesn't_ update the wordWrapWidth, so be aware of the relationship between the two.
717+
*
718+
* Call this method with nothing defined for any of the parameters to reset an existing textBounds.
719+
*
720+
* @method Phaser.Text#setTextBounds
721+
* @param {number} [x] - The x coordinate of the Text Bounds region.
722+
* @param {number} [y] - The y coordinate of the Text Bounds region.
723+
* @param {number} [width] - The width of the Text Bounds region.
724+
* @param {number} [height] - The height of the Text Bounds region.
725+
* @return {Phaser.Text} This Text instance.
726+
*/
727+
Phaser.Text.prototype.setTextBounds = function (x, y, width, height) {
728+
729+
if (typeof x === 'undefined')
730+
{
731+
this.textBounds = null;
732+
}
733+
else
734+
{
735+
if (!this.textBounds)
736+
{
737+
this.textBounds = new Phaser.Rectangle(x, y, width, height);
738+
}
739+
else
740+
{
741+
this.textBounds.setTo(x, y, width, height);
742+
}
743+
}
744+
745+
this.updateTexture();
746+
747+
return this;
748+
669749
};
670750

671751
/**
@@ -676,15 +756,60 @@ Phaser.Text.prototype.setText = function (text) {
676756
*/
677757
Phaser.Text.prototype.updateTexture = function () {
678758

679-
this.texture.baseTexture.width = this.canvas.width;
680-
this.texture.baseTexture.height = this.canvas.height;
681-
this.texture.crop.width = this.texture.frame.width = this.canvas.width;
682-
this.texture.crop.height = this.texture.frame.height = this.canvas.height;
759+
var base = this.texture.baseTexture;
760+
var crop = this.texture.crop;
761+
var trim = this.texture.trim;
762+
var frame = this.texture.frame;
763+
764+
var w = this.canvas.width;
765+
var h = this.canvas.height;
683766

684-
this._width = this.canvas.width;
685-
this._height = this.canvas.height;
767+
base.width = w;
768+
base.height = h;
769+
770+
crop.width = w;
771+
crop.height = h;
772+
773+
trim.width = w;
774+
trim.height = h;
775+
776+
frame.width = w;
777+
frame.height = h;
778+
779+
this.texture.width = w;
780+
this.texture.height = h;
781+
782+
this._width = w;
783+
this._height = h;
784+
785+
if (this.textBounds)
786+
{
787+
var x = this.textBounds.x;
788+
var y = this.textBounds.y;
789+
790+
// Align the canvas based on the bounds
791+
if (this.style.boundsAlignH === 'right')
792+
{
793+
x = this.textBounds.width - this.canvas.width;
794+
}
795+
else if (this.style.boundsAlignH === 'center')
796+
{
797+
x = this.textBounds.halfWidth - (this.canvas.width / 2);
798+
}
799+
800+
if (this.style.boundsAlignV === 'bottom')
801+
{
802+
y = this.textBounds.height - this.canvas.height;
803+
}
804+
else if (this.style.boundsAlignV === 'middle')
805+
{
806+
y = this.textBounds.halfHeight - (this.canvas.height / 2);
807+
}
808+
809+
trim.x = x;
810+
trim.y = y;
811+
}
686812

687-
// update the dirty base textures
688813
this.texture.baseTexture.dirty();
689814

690815
};
@@ -1090,8 +1215,11 @@ Object.defineProperty(Phaser.Text.prototype, 'fill', {
10901215
});
10911216

10921217
/**
1218+
* Controls the horizontal alignment for multiline text.
1219+
* Can be: 'left', 'center' or 'right'.
1220+
* Does not affect single lines of text. For that please see `setTextBounds`.
10931221
* @name Phaser.Text#align
1094-
* @property {string} align - Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text.
1222+
* @property {string} align
10951223
*/
10961224
Object.defineProperty(Phaser.Text.prototype, 'align', {
10971225

@@ -1111,6 +1239,52 @@ Object.defineProperty(Phaser.Text.prototype, 'align', {
11111239

11121240
});
11131241

1242+
/**
1243+
* Horizontal alignment of the text within the `textBounds`. Can be: 'left', 'center' or 'right'.
1244+
* @name Phaser.Text#boundsAlignH
1245+
* @property {string} boundsAlignH
1246+
*/
1247+
Object.defineProperty(Phaser.Text.prototype, 'boundsAlignH', {
1248+
1249+
get: function() {
1250+
return this.style.boundsAlignH;
1251+
},
1252+
1253+
set: function(value) {
1254+
1255+
if (value !== this.style.boundsAlignH)
1256+
{
1257+
this.style.boundsAlignH = value;
1258+
this.dirty = true;
1259+
}
1260+
1261+
}
1262+
1263+
});
1264+
1265+
/**
1266+
* Vertical alignment of the text within the `textBounds`. Can be: 'top', 'middle' or 'bottom'.
1267+
* @name Phaser.Text#boundsAlignV
1268+
* @property {string} boundsAlignV
1269+
*/
1270+
Object.defineProperty(Phaser.Text.prototype, 'boundsAlignV', {
1271+
1272+
get: function() {
1273+
return this.style.boundsAlignV;
1274+
},
1275+
1276+
set: function(value) {
1277+
1278+
if (value !== this.style.boundsAlignV)
1279+
{
1280+
this.style.boundsAlignV = value;
1281+
this.dirty = true;
1282+
}
1283+
1284+
}
1285+
1286+
});
1287+
11141288
/**
11151289
* @name Phaser.Text#stroke
11161290
* @property {string} stroke - A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00'.

0 commit comments

Comments
 (0)