207 lines
11 KiB
JavaScript
207 lines
11 KiB
JavaScript
"use strict";
|
|
var core = require("@pixi/core"), sprite = require("@pixi/sprite"), text = require("@pixi/text"), HTMLTextStyle = require("./HTMLTextStyle.js");
|
|
const _HTMLText = class _HTMLText2 extends sprite.Sprite {
|
|
/**
|
|
* @param {string} [text] - Text contents
|
|
* @param {PIXI.HTMLTextStyle|PIXI.TextStyle|PIXI.ITextStyle} [style] - Style setting to use.
|
|
* Strongly recommend using an HTMLTextStyle object. Providing a PIXI.TextStyle
|
|
* will convert the TextStyle to an HTMLTextStyle and will no longer be linked.
|
|
*/
|
|
constructor(text2 = "", style = {}) {
|
|
super(core.Texture.EMPTY), this._text = null, this._style = null, this._autoResolution = !0, this.localStyleID = -1, this.dirty = !1, this._updateID = 0, this.ownsStyle = !1;
|
|
const image = new Image(), texture = core.Texture.from(image, {
|
|
scaleMode: core.settings.SCALE_MODE,
|
|
resourceOptions: {
|
|
autoLoad: !1
|
|
}
|
|
});
|
|
texture.orig = new core.Rectangle(), texture.trim = new core.Rectangle(), this.texture = texture;
|
|
const nssvg = "http://www.w3.org/2000/svg", nsxhtml = "http://www.w3.org/1999/xhtml", svgRoot = document.createElementNS(nssvg, "svg"), foreignObject = document.createElementNS(nssvg, "foreignObject"), domElement = document.createElementNS(nsxhtml, "div"), styleElement = document.createElementNS(nsxhtml, "style");
|
|
foreignObject.setAttribute("width", "10000"), foreignObject.setAttribute("height", "10000"), foreignObject.style.overflow = "hidden", svgRoot.appendChild(foreignObject), this.maxWidth = _HTMLText2.defaultMaxWidth, this.maxHeight = _HTMLText2.defaultMaxHeight, this._domElement = domElement, this._styleElement = styleElement, this._svgRoot = svgRoot, this._foreignObject = foreignObject, this._foreignObject.appendChild(styleElement), this._foreignObject.appendChild(domElement), this._image = image, this._loadImage = new Image(), this._autoResolution = _HTMLText2.defaultAutoResolution, this._resolution = _HTMLText2.defaultResolution ?? core.settings.RESOLUTION, this.text = text2, this.style = style;
|
|
}
|
|
/**
|
|
* Calculate the size of the output text without actually drawing it.
|
|
* This includes the `padding` in the `style` object.
|
|
* This can be used as a fast-pass to do things like text-fitting.
|
|
* @param {object} [overrides] - Overrides for the text, style, and resolution.
|
|
* @param {string} [overrides.text] - The text to measure, if not specified, the current text is used.
|
|
* @param {PIXI.HTMLTextStyle} [overrides.style] - The style to measure, if not specified, the current style is used.
|
|
* @param {number} [overrides.resolution] - The resolution to measure, if not specified, the current resolution is used.
|
|
* @returns {PIXI.ISize} Width and height of the measured text.
|
|
*/
|
|
measureText(overrides) {
|
|
const { text: text2, style, resolution } = Object.assign({
|
|
text: this._text,
|
|
style: this._style,
|
|
resolution: this._resolution
|
|
}, overrides);
|
|
Object.assign(this._domElement, {
|
|
innerHTML: text2,
|
|
style: style.toCSS(resolution)
|
|
}), this._styleElement.textContent = style.toGlobalCSS(), document.body.appendChild(this._svgRoot);
|
|
const contentBounds = this._domElement.getBoundingClientRect();
|
|
this._svgRoot.remove();
|
|
const { width, height } = contentBounds;
|
|
(width > this.maxWidth || height > this.maxHeight) && console.warn("[HTMLText] Large expanse of text, increase HTMLText.maxWidth or HTMLText.maxHeight property.");
|
|
const contentWidth = Math.min(this.maxWidth, Math.ceil(width)), contentHeight = Math.min(this.maxHeight, Math.ceil(height));
|
|
return this._svgRoot.setAttribute("width", contentWidth.toString()), this._svgRoot.setAttribute("height", contentHeight.toString()), text2 !== this._text && (this._domElement.innerHTML = this._text), style !== this._style && (Object.assign(this._domElement, { style: this._style?.toCSS(resolution) }), this._styleElement.textContent = this._style?.toGlobalCSS()), {
|
|
width: contentWidth + style.padding * 2,
|
|
height: contentHeight + style.padding * 2
|
|
};
|
|
}
|
|
/**
|
|
* Manually refresh the text.
|
|
* @public
|
|
* @param {boolean} respectDirty - Whether to abort updating the
|
|
* text if the Text isn't dirty and the function is called.
|
|
*/
|
|
async updateText(respectDirty = !0) {
|
|
const { style, _image: image, _loadImage: loadImage } = this;
|
|
if (this.localStyleID !== style.styleID && (this.dirty = !0, this.localStyleID = style.styleID), !this.dirty && respectDirty)
|
|
return;
|
|
const { width, height } = this.measureText();
|
|
image.width = loadImage.width = Math.ceil(Math.max(1, width)), image.height = loadImage.height = Math.ceil(Math.max(1, height)), this._updateID++;
|
|
const updateID = this._updateID;
|
|
await new Promise((resolve) => {
|
|
loadImage.onload = async () => {
|
|
if (updateID < this._updateID) {
|
|
resolve();
|
|
return;
|
|
}
|
|
await style.onBeforeDraw(), image.src = loadImage.src, loadImage.onload = null, loadImage.src = "", this.updateTexture(), resolve();
|
|
};
|
|
const svgURL = new XMLSerializer().serializeToString(this._svgRoot);
|
|
loadImage.src = `data:image/svg+xml;charset=utf8,${encodeURIComponent(svgURL)}`;
|
|
});
|
|
}
|
|
/** The raw image element that is rendered under-the-hood. */
|
|
get source() {
|
|
return this._image;
|
|
}
|
|
/**
|
|
* Update the texture resource.
|
|
* @private
|
|
*/
|
|
updateTexture() {
|
|
const { style, texture, _image: image, resolution } = this, { padding } = style, { baseTexture } = texture;
|
|
texture.trim.width = texture._frame.width = image.width / resolution, texture.trim.height = texture._frame.height = image.height / resolution, texture.trim.x = -padding, texture.trim.y = -padding, texture.orig.width = texture._frame.width - padding * 2, texture.orig.height = texture._frame.height - padding * 2, this._onTextureUpdate(), baseTexture.setRealSize(image.width, image.height, resolution), this.dirty = !1;
|
|
}
|
|
/**
|
|
* Renders the object using the WebGL renderer
|
|
* @param {PIXI.Renderer} renderer - The renderer
|
|
* @private
|
|
*/
|
|
_render(renderer) {
|
|
this._autoResolution && this._resolution !== renderer.resolution && (this._resolution = renderer.resolution, this.dirty = !0), this.updateText(!0), super._render(renderer);
|
|
}
|
|
/**
|
|
* Renders the object using the Canvas Renderer.
|
|
* @private
|
|
* @param {PIXI.CanvasRenderer} renderer - The renderer
|
|
*/
|
|
_renderCanvas(renderer) {
|
|
this._autoResolution && this._resolution !== renderer.resolution && (this._resolution = renderer.resolution, this.dirty = !0), this.updateText(!0), super._renderCanvas(renderer);
|
|
}
|
|
/**
|
|
* Get the local bounds.
|
|
* @param {PIXI.Rectangle} rect - Input rectangle.
|
|
* @returns {PIXI.Rectangle} Local bounds
|
|
*/
|
|
getLocalBounds(rect) {
|
|
return this.updateText(!0), super.getLocalBounds(rect);
|
|
}
|
|
_calculateBounds() {
|
|
this.updateText(!0), this.calculateVertices(), this._bounds.addQuad(this.vertexData);
|
|
}
|
|
/**
|
|
* Handle dirty style changes
|
|
* @private
|
|
*/
|
|
_onStyleChange() {
|
|
this.dirty = !0;
|
|
}
|
|
/**
|
|
* Destroy this Text object. Don't use after calling.
|
|
* @param {boolean|object} options - Same as Sprite destroy options.
|
|
*/
|
|
destroy(options) {
|
|
typeof options == "boolean" && (options = { children: options }), options = Object.assign({}, _HTMLText2.defaultDestroyOptions, options), super.destroy(options);
|
|
const forceClear = null;
|
|
this.ownsStyle && this._style?.cleanFonts(), this._style = forceClear, this._svgRoot?.remove(), this._svgRoot = forceClear, this._domElement?.remove(), this._domElement = forceClear, this._foreignObject?.remove(), this._foreignObject = forceClear, this._styleElement?.remove(), this._styleElement = forceClear, this._loadImage.src = "", this._loadImage.onload = null, this._loadImage = forceClear, this._image.src = "", this._image = forceClear;
|
|
}
|
|
/**
|
|
* Get the width in pixels.
|
|
* @member {number}
|
|
*/
|
|
get width() {
|
|
return this.updateText(!0), Math.abs(this.scale.x) * this._image.width / this.resolution;
|
|
}
|
|
set width(value) {
|
|
this.updateText(!0);
|
|
const s = core.utils.sign(this.scale.x) || 1;
|
|
this.scale.x = s * value / this._image.width / this.resolution, this._width = value;
|
|
}
|
|
/**
|
|
* Get the height in pixels.
|
|
* @member {number}
|
|
*/
|
|
get height() {
|
|
return this.updateText(!0), Math.abs(this.scale.y) * this._image.height / this.resolution;
|
|
}
|
|
set height(value) {
|
|
this.updateText(!0);
|
|
const s = core.utils.sign(this.scale.y) || 1;
|
|
this.scale.y = s * value / this._image.height / this.resolution, this._height = value;
|
|
}
|
|
/** The base style to render with text. */
|
|
get style() {
|
|
return this._style;
|
|
}
|
|
set style(style) {
|
|
this._style !== style && (style = style || {}, style instanceof HTMLTextStyle.HTMLTextStyle ? (this.ownsStyle = !1, this._style = style) : style instanceof text.TextStyle ? (console.warn("[HTMLText] Cloning TextStyle, if this is not what you want, use HTMLTextStyle"), this.ownsStyle = !0, this._style = HTMLTextStyle.HTMLTextStyle.from(style)) : (this.ownsStyle = !0, this._style = new HTMLTextStyle.HTMLTextStyle(style)), this.localStyleID = -1, this.dirty = !0);
|
|
}
|
|
/**
|
|
* Contents of text. This can be HTML text and include tags.
|
|
* @example
|
|
* const text = new HTMLText('This is a <em>styled</em> text!');
|
|
* @member {string}
|
|
*/
|
|
get text() {
|
|
return this._text;
|
|
}
|
|
set text(text2) {
|
|
text2 = String(text2 === "" || text2 === null || text2 === void 0 ? " " : text2), text2 = this.sanitiseText(text2), this._text !== text2 && (this._text = text2, this.dirty = !0);
|
|
}
|
|
/**
|
|
* The resolution / device pixel ratio of the canvas.
|
|
* This is set to automatically match the renderer resolution by default, but can be overridden by setting manually.
|
|
* @member {number}
|
|
* @default 1
|
|
*/
|
|
get resolution() {
|
|
return this._resolution;
|
|
}
|
|
set resolution(value) {
|
|
this._autoResolution = !1, this._resolution !== value && (this._resolution = value, this.dirty = !0);
|
|
}
|
|
/**
|
|
* Sanitise text - replace `<br>` with `<br/>`, ` ` with ` `
|
|
* @param text
|
|
* @see https://www.sitepoint.com/community/t/xhtml-1-0-transitional-xml-parsing-error-entity-nbsp-not-defined/3392/3
|
|
*/
|
|
sanitiseText(text2) {
|
|
return text2.replace(/<br>/gi, "<br/>").replace(/<hr>/gi, "<hr/>").replace(/ /gi, " ");
|
|
}
|
|
};
|
|
_HTMLText.defaultDestroyOptions = {
|
|
texture: !0,
|
|
children: !1,
|
|
baseTexture: !0
|
|
}, /** Default maxWidth, set at construction */
|
|
_HTMLText.defaultMaxWidth = 2024, /** Default maxHeight, set at construction */
|
|
_HTMLText.defaultMaxHeight = 2024, /** Default autoResolution for all HTMLText objects */
|
|
_HTMLText.defaultAutoResolution = !0;
|
|
let HTMLText = _HTMLText;
|
|
exports.HTMLText = HTMLText;
|
|
//# sourceMappingURL=HTMLText.js.map
|