UNPKG

jsge

Version:

Javascript Game Engine

250 lines (223 loc) 6.37 kB
import { DrawShapeObject } from "./DrawShapeObject.js"; import { Rectangle } from "./Primitives.js"; import { DRAW_TYPE, ERROR_CODES } from "../../constants.js"; import { Exception } from "../Exception.js"; import { ImageAtlasPosition } from "../Temp/ImageAtlasPosition.js"; /** * @extends DrawShapeObject * @see {@link DrawObjectFactory} should be created with factory method */ export class DrawTextObject extends DrawShapeObject { #font; #textAlign; #textBaseline; #fillStyle; #strokeStyle; #text; #textMetrics; /** * @type {HTMLCanvasElement} */ #textureCanvas = document.createElement("canvas"); /** * @type {boolean} */ #isTextureUpdated = false; /** * @type {ImageAtlasPosition} */ #atlasPosition; /** * @hideconstructor */ constructor(mapX, mapY, text, font, fillStyle, boxWidth, boxHeight) { super(DRAW_TYPE.TEXT, mapX, mapY); this.#text = text; this.#font = font; this.#fillStyle = fillStyle; this.#textMetrics; this.#calculateCanvasTextureAndMeasurements(boxWidth, boxHeight); } /** * Rectangle text box. * @return {Rectangle} */ get boundariesBox() { const width = this._atlasPos.width, height = this._atlasPos.height; return new Rectangle(this.x, this.y - height, width, height); } get vertices() { const bb = this.boundariesBox; return this._calculateRectVertices(bb.width, bb.height); } /** * @type {string} */ get text() { return this.#text; } set text(value) { if (value !== this.#text) { this.#text = value; this.#calculateCanvasTextureAndMeasurements(); } } /** * @type {string} */ get font() { return this.#font; } set font(value) { if (value !== this.#font) { this.#font = value; this.#calculateCanvasTextureAndMeasurements(); } } /** * @type {string} */ get textAlign() { return this.#textAlign; } set textAlign(value) { if (value !== this.#textAlign) { this.#textAlign = value; this.#calculateCanvasTextureAndMeasurements(); } } /** * @type {string} */ get textBaseline() { return this.#textBaseline; } set textBaseline(value) { if (value !== this.#textBaseline) { this.#textBaseline = value; this.#calculateCanvasTextureAndMeasurements(); } } /** * font color * @type {string} */ get fillStyle() { return this.#fillStyle; } /** * font color */ set fillStyle(value) { if (value !== this.#fillStyle) { this.#fillStyle = value; this.#calculateCanvasTextureAndMeasurements(); } } /** * font stroke color * @type {string} */ get strokeStyle() { return this.#strokeStyle; } /** * font stroke color */ set strokeStyle(value) { if (value !== this.#strokeStyle) { this.#strokeStyle = value; this.#calculateCanvasTextureAndMeasurements(); } } get scale() { return super.scale; } set scale(value) { if (value !== this.scale) { super.scale = value; this.#calculateCanvasTextureAndMeasurements(); } } /** * @type {TextMetrics} */ get textMetrics() { return this.#textMetrics; } /** * @ignore */ set _textMetrics(value) { this.#textMetrics = value; } /** * @ignore * @returns {HTMLCanvasElement} */ get _textureCanvas() { return this.#textureCanvas; } /** * @ignore * @returns {ImageAtlasPosition} */ get _atlasPos() { return this.#atlasPosition; } get _isTextureUpdated() { return this.#isTextureUpdated; } _setTextureUpdated() { this.#isTextureUpdated = false; } /** * * @returns {void} */ #calculateCanvasTextureAndMeasurements(atlasWidth, atlasHeight) { const ctx = this._textureCanvas.getContext("2d", { willReadFrequently: true }); // cpu counting instead gpu if (ctx) { //ctx.clearRect(0, 0, this.#textureCanvas.width, this.#textureCanvas.height); ctx.font = this.font; const newTextMetrics = ctx.measureText(this.text); if (this._atlasPos && this.#isTextMetricsNotChanged(newTextMetrics)) { atlasWidth = this._atlasPos.width; atlasHeight = this._atlasPos.height; } else { if (!atlasWidth) { atlasWidth = Math.floor(newTextMetrics.width); } if (!atlasHeight) { atlasHeight = Math.floor(newTextMetrics.fontBoundingBoxAscent + newTextMetrics.fontBoundingBoxDescent); } this.#atlasPosition = new ImageAtlasPosition(atlasWidth, atlasHeight); this._textMetrics = newTextMetrics; } ctx.canvas.width = atlasWidth; ctx.canvas.height = atlasHeight; // after canvas resize, have to cleanup and set the font again ctx.clearRect(0, 0, atlasWidth, atlasHeight); ctx.font = this.font; ctx.textBaseline = "bottom";// bottom if (this.fillStyle) { ctx.fillStyle = this.fillStyle; ctx.fillText(this.text, 0, atlasHeight); } if (this.strokeStyle) { ctx.strokeStyle = this.strokeStyle; ctx.strokeText(this.text, 0, atlasHeight); } this.#isTextureUpdated = true; // debug canvas //this.#textureCanvas.style.position = "absolute"; //document.body.appendChild(this.#textureCanvas); } else { Exception(ERROR_CODES.UNHANDLED_EXCEPTION, "can't getContext('2d')"); } } #isTextMetricsNotChanged(newTextMetrics) { return (newTextMetrics.fontBoundingBoxAscent === this.textMetrics.fontBoundingBoxAscent); } }