melonjs 10.5.1 → 10.6.1
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.
- package/README.md +4 -6
- package/dist/melonjs.js +4342 -4182
- package/dist/melonjs.min.js +14 -14
- package/dist/melonjs.module.d.ts +250 -267
- package/dist/melonjs.module.js +4752 -4606
- package/package.json +10 -10
- package/src/camera/camera2d.js +11 -10
- package/src/geometries/ellipse.js +1 -1
- package/src/geometries/poly.js +1 -1
- package/src/index.js +1 -1
- package/src/input/pointerevent.js +1 -1
- package/src/level/tiled/TMXLayer.js +1 -1
- package/src/level/tiled/TMXTileMap.js +1 -1
- package/src/level/tiled/renderer/TMXHexagonalRenderer.js +1 -1
- package/src/level/tiled/renderer/TMXIsometricRenderer.js +1 -1
- package/src/level/tiled/renderer/TMXOrthogonalRenderer.js +1 -1
- package/src/level/tiled/renderer/TMXRenderer.js +1 -1
- package/src/level/tiled/renderer/TMXStaggeredRenderer.js +1 -1
- package/src/loader/loadingscreen.js +1 -1
- package/src/math/color.js +1 -1
- package/src/math/matrix2.js +2 -2
- package/src/math/matrix3.js +1 -1
- package/src/math/observable_vector2.js +1 -1
- package/src/math/observable_vector3.js +1 -1
- package/src/math/vector2.js +1 -1
- package/src/math/vector3.js +1 -1
- package/src/particles/emitter.js +1 -1
- package/src/physics/body.js +7 -7
- package/src/renderable/colorlayer.js +1 -1
- package/src/renderable/container.js +11 -1
- package/src/renderable/imagelayer.js +1 -1
- package/src/renderable/nineslicesprite.js +6 -3
- package/src/renderable/renderable.js +18 -1
- package/src/renderable/sprite.js +2 -2
- package/src/state/state.js +6 -6
- package/src/system/pooling.js +150 -155
- package/src/text/bitmaptext.js +35 -91
- package/src/text/text.js +82 -145
- package/src/text/textmetrics.js +168 -0
- package/src/text/textstyle.js +14 -0
- package/src/video/canvas/canvas_renderer.js +21 -5
- package/src/video/renderer.js +1 -3
- package/src/video/texture.js +1 -1
- package/src/video/webgl/buffer/vertex.js +0 -3
- package/src/video/webgl/glshader.js +0 -2
- package/src/video/webgl/webgl_compositor.js +11 -0
- package/src/video/webgl/webgl_renderer.js +58 -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
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
335
|
-
|
|
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
|
-
|
|
338
|
-
setContextStyle(context, this);
|
|
331
|
+
this.isDirty = true;
|
|
339
332
|
|
|
340
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
368
|
-
|
|
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
|
-
//
|
|
426
|
-
this.
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
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.
|
|
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
|
-
*
|
|
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;
|
package/src/video/renderer.js
CHANGED
|
@@ -57,7 +57,7 @@ class Renderer {
|
|
|
57
57
|
/**
|
|
58
58
|
* @ignore
|
|
59
59
|
*/
|
|
60
|
-
this.currentBlendMode = "
|
|
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
|
/**
|