melonjs 10.5.2 → 10.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +4 -6
  2. package/dist/melonjs.js +4314 -4177
  3. package/dist/melonjs.min.js +14 -14
  4. package/dist/melonjs.module.d.ts +242 -267
  5. package/dist/melonjs.module.js +4646 -4523
  6. package/package.json +10 -10
  7. package/src/camera/camera2d.js +11 -10
  8. package/src/geometries/ellipse.js +1 -1
  9. package/src/geometries/poly.js +1 -1
  10. package/src/index.js +1 -1
  11. package/src/input/pointerevent.js +1 -1
  12. package/src/level/tiled/TMXLayer.js +1 -1
  13. package/src/level/tiled/TMXTileMap.js +1 -1
  14. package/src/level/tiled/renderer/TMXHexagonalRenderer.js +1 -1
  15. package/src/level/tiled/renderer/TMXIsometricRenderer.js +1 -1
  16. package/src/level/tiled/renderer/TMXOrthogonalRenderer.js +1 -1
  17. package/src/level/tiled/renderer/TMXRenderer.js +1 -1
  18. package/src/level/tiled/renderer/TMXStaggeredRenderer.js +1 -1
  19. package/src/loader/loadingscreen.js +1 -1
  20. package/src/math/color.js +1 -1
  21. package/src/math/matrix2.js +2 -2
  22. package/src/math/matrix3.js +1 -1
  23. package/src/math/observable_vector2.js +1 -1
  24. package/src/math/observable_vector3.js +1 -1
  25. package/src/math/vector2.js +1 -1
  26. package/src/math/vector3.js +1 -1
  27. package/src/particles/emitter.js +1 -1
  28. package/src/physics/body.js +7 -7
  29. package/src/renderable/colorlayer.js +1 -1
  30. package/src/renderable/container.js +11 -1
  31. package/src/renderable/imagelayer.js +1 -1
  32. package/src/renderable/nineslicesprite.js +6 -3
  33. package/src/renderable/renderable.js +18 -1
  34. package/src/renderable/sprite.js +2 -2
  35. package/src/state/state.js +6 -6
  36. package/src/system/pooling.js +150 -155
  37. package/src/text/bitmaptext.js +30 -86
  38. package/src/text/text.js +82 -145
  39. package/src/text/textmetrics.js +168 -0
  40. package/src/text/textstyle.js +14 -0
  41. package/src/video/canvas/canvas_renderer.js +21 -5
  42. package/src/video/renderer.js +1 -3
  43. package/src/video/texture.js +1 -1
  44. package/src/video/webgl/buffer/vertex.js +0 -3
  45. package/src/video/webgl/glshader.js +0 -2
  46. package/src/video/webgl/webgl_renderer.js +46 -19
package/src/text/text.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import Color from "./../math/color.js";
2
2
  import WebGLRenderer from "./../video/webgl/webgl_renderer.js";
3
- import { renderer, createCanvas } from "./../video/video.js";
4
- import * as stringUtil from "./../utils/string.js";
5
- import pool from "./../system/pooling.js";
3
+ import { renderer as globalRenderer, createCanvas } from "./../video/video.js";
4
+ import { trimRight } from "./../utils/string.js";
5
+ import * as pool from "./../system/pooling.js";
6
6
  import Renderable from "./../renderable/renderable.js";
7
7
  import { nextPowerOfTwo } from "./../math/math.js";
8
-
8
+ import setContextStyle from "./textstyle.js";
9
+ import TextMetrics from "./textmetrics.js";
9
10
 
10
11
  /*
11
12
  * ASCII Table
@@ -19,19 +20,13 @@ import { nextPowerOfTwo } from "./../math/math.js";
19
20
  var runits = ["ex", "em", "pt", "px"];
20
21
  var toPX = [12, 24, 0.75, 1];
21
22
 
22
- /**
23
- * apply the current font style to the given context
24
- * @ignore
25
- */
26
- var setContextStyle = function(context, font, stroke = false) {
27
- context.font = font.font;
28
- context.fillStyle = font.fillStyle.toRGBA();
29
- if (stroke === true) {
30
- context.strokeStyle = font.strokeStyle.toRGBA();
31
- context.lineWidth = font.lineWidth;
23
+ // return a valid 2d context for Text rendering/styling
24
+ var getContext2d = function (renderer, text) {
25
+ if (text.offScreenCanvas === true) {
26
+ return text.context;
27
+ } else {
28
+ return renderer.getFontContext();
32
29
  }
33
- context.textAlign = font.textAlign;
34
- context.textBaseline = font.textBaseline;
35
30
  };
36
31
 
37
32
  /**
@@ -54,6 +49,7 @@ class Text extends Renderable {
54
49
  * @param {number} [settings.lineHeight=1.0] line spacing height
55
50
  * @param {Vector2d} [settings.anchorPoint={x:0.0, y:0.0}] anchor point to draw the text at
56
51
  * @param {boolean} [settings.offScreenCanvas=false] whether to draw the font to an individual "cache" texture first
52
+ * @param {number} [settings.wordWrapWidth] the maximum length in CSS pixel for a single segment of text
57
53
  * @param {(string|string[])} [settings.text=""] a string, or an array of strings
58
54
  * @example
59
55
  * var font = new me.Text(0, 0, {font: "Arial", size: 8, fillStyle: this.color});
@@ -72,7 +68,6 @@ class Text extends Renderable {
72
68
  * @public
73
69
  * @type {Color}
74
70
  * @default black
75
- * @name Text#fillStyle
76
71
  */
77
72
  if (typeof settings.fillStyle !== "undefined") {
78
73
  if (settings.fillStyle instanceof Color) {
@@ -90,7 +85,6 @@ class Text extends Renderable {
90
85
  * @public
91
86
  * @type {Color}
92
87
  * @default black
93
- * @name Text#strokeStyle
94
88
  */
95
89
  if (typeof settings.strokeStyle !== "undefined") {
96
90
  if (settings.strokeStyle instanceof Color) {
@@ -108,7 +102,6 @@ class Text extends Renderable {
108
102
  * @public
109
103
  * @type {number}
110
104
  * @default 1
111
- * @name Text#lineWidth
112
105
  */
113
106
  this.lineWidth = settings.lineWidth || 1;
114
107
 
@@ -118,7 +111,6 @@ class Text extends Renderable {
118
111
  * @public
119
112
  * @type {string}
120
113
  * @default "left"
121
- * @name Text#textAlign
122
114
  */
123
115
  this.textAlign = settings.textAlign || "left";
124
116
 
@@ -128,7 +120,6 @@ class Text extends Renderable {
128
120
  * @public
129
121
  * @type {string}
130
122
  * @default "top"
131
- * @name Text#textBaseline
132
123
  */
133
124
  this.textBaseline = settings.textBaseline || "top";
134
125
 
@@ -138,7 +129,6 @@ class Text extends Renderable {
138
129
  * @public
139
130
  * @type {number}
140
131
  * @default 1.0
141
- * @name Text#lineHeight
142
132
  */
143
133
  this.lineHeight = settings.lineHeight || 1.0;
144
134
 
@@ -149,10 +139,18 @@ class Text extends Renderable {
149
139
  * @public
150
140
  * @type {boolean}
151
141
  * @default false
152
- * @name Text#offScreenCanvas
153
142
  */
154
143
  this.offScreenCanvas = false;
155
144
 
145
+ /**
146
+ * the maximum length in CSS pixel for a single segment of text.
147
+ * (use -1 to disable word wrapping)
148
+ * @public
149
+ * @type {number}
150
+ * @default -1
151
+ */
152
+ this.wordWrapWidth = settings.wordWrapWidth || -1;
153
+
156
154
  /**
157
155
  * the text to be displayed
158
156
  * @private
@@ -163,9 +161,7 @@ class Text extends Renderable {
163
161
  * the font size (in px)
164
162
  * @public
165
163
  * @type {number}
166
- * @name fontSize
167
164
  * @default 10
168
- * @memberof Text
169
165
  */
170
166
  this.fontSize = 10;
171
167
 
@@ -198,19 +194,19 @@ class Text extends Renderable {
198
194
  this.context = this.canvas.getContext("2d");
199
195
  }
200
196
 
197
+ // instance to text metrics functions
198
+ this.metrics = new TextMetrics(this);
199
+
201
200
  // set the text
202
201
  this.setText(settings.text);
203
-
204
- // force update bounds on object creation
205
- this.update(0);
206
202
  }
207
203
 
208
204
  /** @ignore */
209
205
  onDeactivateEvent() {
210
206
  // free the canvas and potential corresponding texture when deactivated
211
207
  if (this.offScreenCanvas === true) {
212
- renderer.currentCompositor.deleteTexture2D(renderer.currentCompositor.getTexture2D(this.glTextureUnit));
213
- renderer.cache.delete(this.canvas);
208
+ globalRenderer.currentCompositor.deleteTexture2D(globalRenderer.currentCompositor.getTexture2D(this.glTextureUnit));
209
+ globalRenderer.cache.delete(this.canvas);
214
210
  this.canvas.width = this.canvas.height = 0;
215
211
  this.context = undefined;
216
212
  this.canvas = undefined;
@@ -220,9 +216,6 @@ class Text extends Renderable {
220
216
 
221
217
  /**
222
218
  * make the font bold
223
- * @name bold
224
- * @memberof Text.prototype
225
- * @function
226
219
  * @returns {Text} this object for chaining
227
220
  */
228
221
  bold() {
@@ -233,9 +226,6 @@ class Text extends Renderable {
233
226
 
234
227
  /**
235
228
  * make the font italic
236
- * @name italic
237
- * @memberof Text.prototype
238
- * @function
239
229
  * @returns {Text} this object for chaining
240
230
  */
241
231
  italic() {
@@ -246,9 +236,6 @@ class Text extends Renderable {
246
236
 
247
237
  /**
248
238
  * set the font family and size
249
- * @name setFont
250
- * @memberof Text.prototype
251
- * @function
252
239
  * @param {string} font a CSS font name
253
240
  * @param {number|string} [size=10] size in px, or size + suffix (px, em, pt)
254
241
  * @returns {Text} this object for chaining
@@ -256,7 +243,7 @@ class Text extends Renderable {
256
243
  * font.setFont("Arial", 20);
257
244
  * font.setFont("Arial", "1.5em");
258
245
  */
259
- setFont(font, size) {
246
+ setFont(font, size = 10) {
260
247
  // font name and type
261
248
  var font_names = font.split(",").map(function (value) {
262
249
  value = value.trim();
@@ -290,119 +277,75 @@ class Text extends Renderable {
290
277
 
291
278
  /**
292
279
  * change the text to be displayed
293
- * @name setText
294
- * @memberof Text.prototype
295
- * @function
296
280
  * @param {number|string|string[]} value a string, or an array of strings
297
281
  * @returns {Text} this object for chaining
298
282
  */
299
283
  setText(value = "") {
284
+ var bounds = this.getBounds();
285
+
286
+ // set the next text
300
287
  if (this._text.toString() !== value.toString()) {
301
288
  if (!Array.isArray(value)) {
302
289
  this._text = ("" + value).split("\n");
303
290
  } else {
304
291
  this._text = value;
305
292
  }
306
- this.isDirty = true;
307
293
  }
308
294
 
309
- return this;
310
- }
295
+ // word wrap if necessary
296
+ if (this._text.length > 0 && this.wordWrapWidth > 0) {
297
+ this._text = this.metrics.wordWrap(this._text, this.wordWrapWidth, getContext2d(globalRenderer, this));
298
+ }
311
299
 
312
- /**
313
- * measure the given text size in pixels
314
- * @name measureText
315
- * @memberof Text.prototype
316
- * @function
317
- * @param {CanvasRenderer|WebGLRenderer} [renderer] reference to the active renderer
318
- * @param {string} [text] the text to be measured
319
- * @param {Rect|Bounds} [ret] a object in which to store the text metrics
320
- * @returns {TextMetrics} a TextMetrics object with two properties: `width` and `height`, defining the output dimensions
321
- */
322
- measureText(renderer, text, ret) {
323
- var context;
324
- var textMetrics = ret || this.getBounds();
325
- var lineHeight = this.fontSize * this.lineHeight;
326
- var strings = typeof text !== "undefined" ? ("" + (text)).split("\n") : this._text;
300
+ // calculcate the text size and update the bounds accordingly
301
+ bounds.addBounds(this.metrics.measureText(this._text, getContext2d(globalRenderer, this)), true);
327
302
 
303
+ // update the offScreenCanvas texture if required
328
304
  if (this.offScreenCanvas === true) {
329
- context = this.context;
330
- } else {
331
- context = renderer.getFontContext();
332
- }
305
+ var width = Math.ceil(this.metrics.width),
306
+ height = Math.ceil(this.metrics.height);
307
+
308
+ if (globalRenderer instanceof WebGLRenderer) {
309
+ // invalidate the previous corresponding texture so that it can reuploaded once changed
310
+ this.glTextureUnit = globalRenderer.cache.getUnit(globalRenderer.cache.get(this.canvas));
311
+ globalRenderer.currentCompositor.unbindTexture2D(null, this.glTextureUnit);
312
+
313
+ if (globalRenderer.WebGLVersion === 1) {
314
+ // round size to next Pow2
315
+ width = nextPowerOfTwo(this.metrics.width);
316
+ height = nextPowerOfTwo(this.metrics.height);
317
+ }
318
+ }
333
319
 
334
- // save the previous context
335
- context.save();
320
+ // resize the cache canvas if necessary
321
+ if (this.canvas.width < width || this.canvas.height < height) {
322
+ this.canvas.width = width;
323
+ this.canvas.height = height;
324
+ // resizing the canvas will automatically clear its content
325
+ } else {
326
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
327
+ }
328
+ this._drawFont(this.context, this._text, this.pos.x - this.metrics.x, this.pos.y - this.metrics.y, false);
329
+ }
336
330
 
337
- // apply the style font
338
- setContextStyle(context, this);
331
+ this.isDirty = true;
339
332
 
340
- // compute the bounding box size
341
- this.height = this.width = 0;
342
- for (var i = 0; i < strings.length; i++) {
343
- this.width = Math.max(context.measureText(stringUtil.trimRight(""+strings[i])).width, this.width);
344
- this.height += lineHeight;
345
- }
346
- textMetrics.width = Math.ceil(this.width);
347
- textMetrics.height = Math.ceil(this.height);
348
-
349
- // compute the bounding box position
350
- textMetrics.x = Math.floor((this.textAlign === "right" ? this.pos.x - this.width : (
351
- this.textAlign === "center" ? this.pos.x - (this.width / 2) : this.pos.x
352
- )));
353
- textMetrics.y = Math.floor((this.textBaseline.search(/^(top|hanging)$/) === 0) ? this.pos.y : (
354
- this.textBaseline === "middle" ? this.pos.y - (textMetrics.height / 2) : this.pos.y - textMetrics.height
355
- ));
356
-
357
- // restore the context
358
- context.restore();
359
-
360
- // returns the Font bounds me.Rect by default
361
- return textMetrics;
333
+ return this;
362
334
  }
363
335
 
364
336
  /**
365
- * @ignore
337
+ * measure the given text size in pixels
338
+ * @param {CanvasRenderer|WebGLRenderer} [renderer] reference to the active renderer
339
+ * @param {string} [text] the text to be measured
340
+ * @returns {TextMetrics} a TextMetrics object defining the dimensions of the given piece of text
366
341
  */
367
- update(/* dt */) {
368
- if (this.isDirty === true) {
369
- var bounds = this.measureText(renderer);
370
- if (this.offScreenCanvas === true) {
371
- var width = Math.round(bounds.width),
372
- height = Math.round(bounds.height);
373
-
374
- if (renderer instanceof WebGLRenderer) {
375
- // invalidate the previous corresponding texture so that it can reuploaded once changed
376
- this.glTextureUnit = renderer.cache.getUnit(renderer.cache.get(this.canvas));
377
- renderer.currentCompositor.unbindTexture2D(null, this.glTextureUnit);
378
-
379
- if (renderer.WebGLVersion === 1) {
380
- // round size to next Pow2
381
- width = nextPowerOfTwo(bounds.width);
382
- height = nextPowerOfTwo(bounds.height);
383
- }
384
- }
385
-
386
- // resize the cache canvas if necessary
387
- if (this.canvas.width < width || this.canvas.height < height) {
388
- this.canvas.width = width;
389
- this.canvas.height = height;
390
- // resizing the canvas will automatically clear its content
391
- } else {
392
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
393
- }
394
- this._drawFont(this.context, this._text, this.pos.x - bounds.x, this.pos.y - bounds.y, false);
395
-
396
- }
397
- }
398
- return this.isDirty;
342
+ measureText(renderer = globalRenderer, text = this._text) {
343
+ return this.metrics.measureText(text, getContext2d(renderer, this));
399
344
  }
400
345
 
346
+
401
347
  /**
402
348
  * draw a text at the specified coord
403
- * @name draw
404
- * @memberof Text.prototype
405
- * @function
406
349
  * @param {CanvasRenderer|WebGLRenderer} renderer Reference to the destination renderer instance
407
350
  * @param {string} [text]
408
351
  * @param {number} [x]
@@ -412,8 +355,6 @@ class Text extends Renderable {
412
355
  draw(renderer, text, x, y, stroke) {
413
356
  // "hacky patch" for backward compatibilty
414
357
  if (typeof this.ancestor === "undefined") {
415
- // update text cache
416
- this.setText(text);
417
358
 
418
359
  // update position if changed
419
360
  if (this.pos.x !== x || this.pos.y !== y) {
@@ -422,8 +363,11 @@ class Text extends Renderable {
422
363
  this.isDirty = true;
423
364
  }
424
365
 
425
- // force update bounds
426
- this.update(0);
366
+ // update text cache
367
+ this.setText(text);
368
+
369
+ x = this.metrics.x;
370
+ y = this.metrics.y;
427
371
 
428
372
  // save the previous context
429
373
  renderer.save();
@@ -432,20 +376,20 @@ class Text extends Renderable {
432
376
  renderer.setGlobalAlpha(renderer.globalAlpha() * this.getOpacity());
433
377
 
434
378
  } else {
435
- // added directly to an object container
379
+ // added directly to an object container
436
380
  x = this.pos.x;
437
381
  y = this.pos.y;
438
382
  }
439
383
 
384
+ // clamp to pixel grid if required
440
385
  if (renderer.settings.subPixel === false) {
441
- // clamp to pixel grid if required
442
386
  x = ~~x;
443
387
  y = ~~y;
444
388
  }
445
389
 
446
390
  // draw the text
447
391
  if (this.offScreenCanvas === true) {
448
- renderer.drawImage(this.canvas, this.getBounds().x, this.getBounds().y);
392
+ renderer.drawImage(this.canvas, x, y);
449
393
  } else {
450
394
  renderer.drawFont(this._drawFont(renderer.getFontContext(), this._text, x, y, stroke));
451
395
  }
@@ -456,19 +400,12 @@ class Text extends Renderable {
456
400
  // restore previous context
457
401
  renderer.restore();
458
402
  }
459
-
460
- // clear the dirty flag here for
461
- // backward compatibility
462
- this.isDirty = false;
463
403
  }
464
404
 
465
405
  /**
466
406
  * draw a stroke text at the specified coord, as defined <br>
467
407
  * by the `lineWidth` and `fillStroke` properties. <br>
468
408
  * Note : using drawStroke is not recommended for performance reasons
469
- * @name drawStroke
470
- * @memberof Text.prototype
471
- * @function
472
409
  * @param {CanvasRenderer|WebGLRenderer} renderer Reference to the destination renderer instance
473
410
  * @param {string} text
474
411
  * @param {number} x
@@ -484,15 +421,14 @@ class Text extends Renderable {
484
421
  _drawFont(context, text, x, y, stroke = false) {
485
422
  setContextStyle(context, this, stroke);
486
423
 
487
- var lineHeight = this.fontSize * this.lineHeight;
488
424
  for (var i = 0; i < text.length; i++) {
489
- var string = stringUtil.trimRight(""+text[i]);
425
+ var string = trimRight(text[i]);
490
426
  // draw the string
491
427
  context[stroke ? "strokeText" : "fillText"](string, x, y);
492
428
  // add leading space
493
- y += lineHeight;
429
+ y += this.metrics.lineHeight();
494
430
  }
495
- return this.getBounds();
431
+ return this.metrics;
496
432
  }
497
433
 
498
434
  /**
@@ -503,6 +439,7 @@ class Text extends Renderable {
503
439
  pool.push(this.fillStyle);
504
440
  pool.push(this.strokeStyle);
505
441
  this.fillStyle = this.strokeStyle = undefined;
442
+ this.metrics = undefined;
506
443
  this._text.length = 0;
507
444
  super.destroy();
508
445
  }
@@ -0,0 +1,168 @@
1
+ import Bounds from "./../physics/bounds.js";
2
+ import { trimRight } from "./../utils/string.js";
3
+ import Text from "./text.js";
4
+ //import BitmapText from "./bitmaptext.js";
5
+ import setContextStyle from "./textstyle.js";
6
+
7
+
8
+ /**
9
+ * @classdesc
10
+ * a Text Metrics object that contains helper for text manipulation
11
+ * @augments Bounds
12
+ */
13
+ class TextMetrics extends Bounds {
14
+
15
+ /**
16
+ * @param {Text|BitmapText} ancestor the parent object that contains this TextMetrics object
17
+ */
18
+ constructor(ancestor) {
19
+
20
+ // parent constructor
21
+ super();
22
+
23
+ /**
24
+ * a reference to the parent object that contains this TextMetrics object
25
+ * @public
26
+ * @type {Renderable}
27
+ * @default undefined
28
+ */
29
+ this.ancestor = ancestor;
30
+
31
+ this.setMinMax(0, 0, 0, 0);
32
+ }
33
+
34
+ /**
35
+ * Returns the height of a segment of inline text in CSS pixels.
36
+ * @returns {number} the height of a segment of inline text in CSS pixels.
37
+ */
38
+ lineHeight() {
39
+ if (this.ancestor instanceof Text) {
40
+ return this.ancestor.fontSize * this.ancestor.lineHeight;
41
+ } else { // it's a BitmapText
42
+ return this.ancestor.fontData.capHeight * this.ancestor.lineHeight * this.ancestor.fontScale.y;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Returns the width of the given segment of inline text in CSS pixels.
48
+ * @param {string} text the text to be measured
49
+ * @param {CanvasRenderingContext2D} [context] reference to an active 2d context for canvas rendering
50
+ * @returns {number} the width of the given segment of inline text in CSS pixels.
51
+ */
52
+ lineWidth(text, context) {
53
+ if (this.ancestor instanceof Text) {
54
+ return context.measureText(text).width;
55
+ } else { // it's a BitmapText
56
+ var characters = text.split("");
57
+ var width = 0;
58
+ var lastGlyph = null;
59
+ for (var i = 0; i < characters.length; i++) {
60
+ var ch = characters[i].charCodeAt(0);
61
+ var glyph = this.ancestor.fontData.glyphs[ch];
62
+ var kerning = (lastGlyph && lastGlyph.kerning) ? lastGlyph.getKerning(ch) : 0;
63
+ width += (glyph.xadvance + kerning) * this.ancestor.fontScale.x;
64
+ lastGlyph = glyph;
65
+ }
66
+ return width;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * measure the given text size in CSS pixels
72
+ * @param {string} text the text to be measured
73
+ * @param {CanvasRenderingContext2D} [context] reference to an active 2d context for canvas rendering
74
+ * @returns {TextMetrics} this
75
+ */
76
+ measureText(text, context) {
77
+ var strings;
78
+
79
+ if (!Array.isArray(text)) {
80
+ strings = ("" + text).split("\n");
81
+ } else {
82
+ strings = text;
83
+ }
84
+
85
+ if (typeof context !== "undefined") {
86
+ // save the previous context
87
+ context.save();
88
+
89
+ // apply the style font
90
+ setContextStyle(context, this.ancestor);
91
+ }
92
+
93
+ // compute the bounding box size
94
+ this.width = this.height = 0;
95
+
96
+ for (var i = 0; i < strings.length; i++) {
97
+ this.width = Math.max(this.lineWidth(trimRight(strings[i]), context), this.width);
98
+ this.height += this.lineHeight();
99
+ }
100
+ this.width = Math.ceil(this.width);
101
+ this.height = Math.ceil(this.height);
102
+
103
+ // compute the bounding box position
104
+ this.x = Math.floor((this.ancestor.textAlign === "right" ? this.ancestor.pos.x - this.width : (
105
+ this.ancestor.textAlign === "center" ? this.ancestor.pos.x - (this.width / 2) : this.ancestor.pos.x
106
+ )));
107
+ this.y = Math.floor((this.ancestor.textBaseline.search(/^(top|hanging)$/) === 0) ? this.ancestor.pos.y : (
108
+ this.ancestor.textBaseline === "middle" ? this.ancestor.pos.y - (this.lineHeight() / 2) : this.ancestor.pos.y - this.lineHeight()
109
+ ));
110
+
111
+ if (typeof context !== "undefined") {
112
+ // restore the context
113
+ context.restore();
114
+ }
115
+
116
+ return this;
117
+ }
118
+
119
+ /**
120
+ * wrap the given text based on the given width
121
+ * @param {string|string[]} text the text to be wrapped
122
+ * @param {number} width maximum width of one segment of text in css pixel
123
+ * @param {CanvasRenderingContext2D} [context] reference to an active 2d context for canvas rendering
124
+ * @returns {string[]} an array of string representing wrapped text
125
+ */
126
+ wordWrap(text, width, context) {
127
+ var words;
128
+ var currentLine = "";
129
+ var output = [];
130
+
131
+ if (Array.isArray(text)) {
132
+ // join into a single string
133
+ text = text.join(" ");
134
+ }
135
+ // word splitting to be improved as it replaces \n by space if present
136
+ words = text.replace(/[\r\n]+/g, " ").split(" ");
137
+
138
+ if (typeof context !== "undefined") {
139
+ // save the previous context
140
+ context.save();
141
+
142
+ // apply the style font
143
+ setContextStyle(context, this.ancestor);
144
+ }
145
+
146
+ for (let i = 0; i < words.length; i++) {
147
+ var word = words[i];
148
+ var lineWidth = this.lineWidth(currentLine + word + " ", context);
149
+ if (lineWidth < width) {
150
+ // add the word to the current line
151
+ currentLine += word + " ";
152
+ } else {
153
+ output.push(currentLine + "\n");
154
+ currentLine = word + " ";
155
+ }
156
+ }
157
+ // last line
158
+ output.push(currentLine);
159
+
160
+ if (typeof context !== "undefined") {
161
+ // restore the context
162
+ context.restore();
163
+ }
164
+
165
+ return output;
166
+ }
167
+ }
168
+ export default TextMetrics;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * apply the current text style to the given context
3
+ * @ignore
4
+ */
5
+ export default function setContextStyle(context, style, stroke = false) {
6
+ context.font = style.font;
7
+ context.fillStyle = style.fillStyle.toRGBA();
8
+ if (stroke === true) {
9
+ context.strokeStyle = style.strokeStyle.toRGBA();
10
+ context.lineWidth = style.lineWidth;
11
+ }
12
+ context.textAlign = style.textAlign;
13
+ context.textBaseline = style.textBaseline;
14
+ };
@@ -54,8 +54,6 @@ class CanvasRenderer extends Renderer {
54
54
  // enable the tile texture seam fix with the canvas renderer
55
55
  this.uvOffset = 1;
56
56
  }
57
-
58
- return this;
59
57
  }
60
58
 
61
59
  /**
@@ -80,17 +78,35 @@ class CanvasRenderer extends Renderer {
80
78
  }
81
79
 
82
80
  /**
83
- * Set a blend mode for the given context
81
+ * set a blend mode for the given context. <br>
82
+ * Supported blend mode between Canvas and WebGL remderer : <br>
83
+ * - "normal" : this is the default mode and draws new content on top of the existing content <br>
84
+ * <img src="images/normal-blendmode.png" width="510"/> <br>
85
+ * - "multiply" : the pixels of the top layer are multiplied with the corresponding pixel of the bottom layer. A darker picture is the result. <br>
86
+ * <img src="images/multiply-blendmode.png" width="510"/> <br>
87
+ * - "lighter" : where both content overlap the color is determined by adding color values. <br>
88
+ * <img src="images/lighter-blendmode.png" width="510"/> <br>
89
+ * - "screen" : The pixels are inverted, multiplied, and inverted again. A lighter picture is the result (opposite of multiply) <br>
90
+ * <img src="images/screen-blendmode.png" width="510"/> <br>
84
91
  * @name setBlendMode
92
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
85
93
  * @memberof CanvasRenderer.prototype
86
94
  * @function
87
- * @param {string} [mode="normal"] blend mode : "normal", "multiply"
95
+ * @param {string} [mode="normal"] blend mode : "normal", "multiply", "lighter, "screen"
88
96
  * @param {CanvasRenderingContext2D} [context]
89
97
  */
90
- setBlendMode(mode, context) {
98
+ setBlendMode(mode = "normal", context) {
91
99
  context = context || this.getContext();
92
100
  this.currentBlendMode = mode;
93
101
  switch (mode) {
102
+ case "screen" :
103
+ context.globalCompositeOperation = "screen";
104
+ break;
105
+
106
+ case "lighter" :
107
+ context.globalCompositeOperation = "lighter";
108
+ break;
109
+
94
110
  case "multiply" :
95
111
  context.globalCompositeOperation = "multiply";
96
112
  break;
@@ -57,7 +57,7 @@ class Renderer {
57
57
  /**
58
58
  * @ignore
59
59
  */
60
- this.currentBlendMode = "normal";
60
+ this.currentBlendMode = "none";
61
61
 
62
62
  // create the main screen canvas
63
63
  if (device.ejecta === true) {
@@ -92,8 +92,6 @@ class Renderer {
92
92
  event.on(event.GAME_RESET, () => {
93
93
  renderer.reset();
94
94
  });
95
-
96
- return this;
97
95
  }
98
96
 
99
97
  /**