UNPKG

pixi.js

Version:

<p align="center"> <a href="https://pixijs.com" target="_blank" rel="noopener noreferrer"> <img height="150" src="https://files.pixijs.download/branding/pixijs-logo-transparent-dark.svg?v=1" alt="PixiJS logo"> </a> </p> <br/> <p align="center">

496 lines (492 loc) 15.5 kB
'use strict'; var EventEmitter = require('eventemitter3'); var Color = require('../../color/Color.js'); var deprecation = require('../../utils/logging/deprecation.js'); var FillGradient = require('../graphics/shared/fill/FillGradient.js'); var FillPattern = require('../graphics/shared/fill/FillPattern.js'); var GraphicsContext = require('../graphics/shared/GraphicsContext.js'); var convertFillInputToFillStyle = require('../graphics/shared/utils/convertFillInputToFillStyle.js'); var generateTextStyleKey = require('./utils/generateTextStyleKey.js'); "use strict"; const _TextStyle = class _TextStyle extends EventEmitter { constructor(style = {}) { super(); convertV7Tov8Style(style); const fullStyle = { ..._TextStyle.defaultTextStyle, ...style }; for (const key in fullStyle) { const thisKey = key; this[thisKey] = fullStyle[key]; } this.update(); } /** * Alignment for multiline text, does not affect single line text. * @type {'left'|'center'|'right'|'justify'} */ get align() { return this._align; } set align(value) { this._align = value; this.update(); } /** Indicates if lines can be wrapped within words, it needs wordWrap to be set to true. */ get breakWords() { return this._breakWords; } set breakWords(value) { this._breakWords = value; this.update(); } /** Set a drop shadow for the text. */ get dropShadow() { return this._dropShadow; } set dropShadow(value) { if (value !== null && typeof value === "object") { this._dropShadow = this._createProxy({ ..._TextStyle.defaultDropShadow, ...value }); } else { this._dropShadow = value ? this._createProxy({ ..._TextStyle.defaultDropShadow }) : null; } this.update(); } /** The font family, can be a single font name, or a list of names where the first is the preferred font. */ get fontFamily() { return this._fontFamily; } set fontFamily(value) { this._fontFamily = value; this.update(); } /** The font size (as a number it converts to px, but as a string, equivalents are '26px','20pt','160%' or '1.6em') */ get fontSize() { return this._fontSize; } set fontSize(value) { if (typeof value === "string") { this._fontSize = parseInt(value, 10); } else { this._fontSize = value; } this.update(); } /** * The font style. * @type {'normal'|'italic'|'oblique'} */ get fontStyle() { return this._fontStyle; } set fontStyle(value) { this._fontStyle = value.toLowerCase(); this.update(); } /** * The font variant. * @type {'normal'|'small-caps'} */ get fontVariant() { return this._fontVariant; } set fontVariant(value) { this._fontVariant = value; this.update(); } /** * The font weight. * @type {'normal'|'bold'|'bolder'|'lighter'|'100'|'200'|'300'|'400'|'500'|'600'|'700'|'800'|'900'} */ get fontWeight() { return this._fontWeight; } set fontWeight(value) { this._fontWeight = value; this.update(); } /** The space between lines. */ get leading() { return this._leading; } set leading(value) { this._leading = value; this.update(); } /** The amount of spacing between letters, default is 0. */ get letterSpacing() { return this._letterSpacing; } set letterSpacing(value) { this._letterSpacing = value; this.update(); } /** The line height, a number that represents the vertical space that a letter uses. */ get lineHeight() { return this._lineHeight; } set lineHeight(value) { this._lineHeight = value; this.update(); } /** * Occasionally some fonts are cropped. Adding some padding will prevent this from happening * by adding padding to all sides of the text. * > [!NOTE] This will NOT affect the positioning or bounds of the text. */ get padding() { return this._padding; } set padding(value) { this._padding = value; this.update(); } /** * An optional filter or array of filters to apply to the text, allowing for advanced visual effects. * These filters will be applied to the text as it is created, resulting in faster rendering for static text * compared to applying the filter directly to the text object (which would be applied at run time). * @default null */ get filters() { return this._filters; } set filters(value) { this._filters = value; this.update(); } /** * Trim transparent borders from the text texture. * > [!IMPORTANT] PERFORMANCE WARNING: * > This is a costly operation as it requires scanning pixel alpha values. * > Avoid using `trim: true` for dynamic text, as it could significantly impact performance. */ get trim() { return this._trim; } set trim(value) { this._trim = value; this.update(); } /** * The baseline of the text that is rendered. * @type {'alphabetic'|'top'|'hanging'|'middle'|'ideographic'|'bottom'} */ get textBaseline() { return this._textBaseline; } set textBaseline(value) { this._textBaseline = value; this.update(); } /** * How newlines and spaces should be handled. * Default is 'pre' (preserve, preserve). * * value | New lines | Spaces * --- | --- | --- * 'normal' | Collapse | Collapse * 'pre' | Preserve | Preserve * 'pre-line' | Preserve | Collapse * @type {'normal'|'pre'|'pre-line'} */ get whiteSpace() { return this._whiteSpace; } set whiteSpace(value) { this._whiteSpace = value; this.update(); } /** Indicates if word wrap should be used. */ get wordWrap() { return this._wordWrap; } set wordWrap(value) { this._wordWrap = value; this.update(); } /** The width at which text will wrap, it needs wordWrap to be set to true. */ get wordWrapWidth() { return this._wordWrapWidth; } set wordWrapWidth(value) { this._wordWrapWidth = value; this.update(); } /** * The fill style that will be used to color the text. * This can be: * - A color string like 'red', '#00FF00', or 'rgba(255,0,0,0.5)' * - A hex number like 0xff0000 for red * - A FillStyle object with properties like { color: 0xff0000, alpha: 0.5 } * - A FillGradient for gradient fills * - A FillPattern for pattern/texture fills * * When using a FillGradient, vertical gradients (angle of 90 degrees) are applied per line of text, * while gradients at any other angle are spread across the entire text body as a whole. * @example * // Vertical gradient applied per line * const verticalGradient = new FillGradient(0, 0, 0, 1) * .addColorStop(0, 0xff0000) * .addColorStop(1, 0x0000ff); * * const text = new Text({ * text: 'Line 1\nLine 2', * style: { fill: verticalGradient } * }); * * To manage the gradient in a global scope, set the textureSpace property of the FillGradient to 'global'. * @type {string|number|FillStyle|FillGradient|FillPattern} */ get fill() { return this._originalFill; } set fill(value) { if (value === this._originalFill) return; this._originalFill = value; if (this._isFillStyle(value)) { this._originalFill = this._createProxy({ ...GraphicsContext.GraphicsContext.defaultFillStyle, ...value }, () => { this._fill = convertFillInputToFillStyle.toFillStyle( { ...this._originalFill }, GraphicsContext.GraphicsContext.defaultFillStyle ); }); } this._fill = convertFillInputToFillStyle.toFillStyle( value === 0 ? "black" : value, GraphicsContext.GraphicsContext.defaultFillStyle ); this.update(); } /** A fillstyle that will be used on the text stroke, e.g., 'blue', '#FCFF00'. */ get stroke() { return this._originalStroke; } set stroke(value) { if (value === this._originalStroke) return; this._originalStroke = value; if (this._isFillStyle(value)) { this._originalStroke = this._createProxy({ ...GraphicsContext.GraphicsContext.defaultStrokeStyle, ...value }, () => { this._stroke = convertFillInputToFillStyle.toStrokeStyle( { ...this._originalStroke }, GraphicsContext.GraphicsContext.defaultStrokeStyle ); }); } this._stroke = convertFillInputToFillStyle.toStrokeStyle(value, GraphicsContext.GraphicsContext.defaultStrokeStyle); this.update(); } _generateKey() { this._styleKey = generateTextStyleKey.generateTextStyleKey(this); return this._styleKey; } update() { this._styleKey = null; this.emit("update", this); } /** Resets all properties to the default values */ reset() { const defaultStyle = _TextStyle.defaultTextStyle; for (const key in defaultStyle) { this[key] = defaultStyle[key]; } } /** @internal */ get styleKey() { return this._styleKey || this._generateKey(); } /** * Creates a new TextStyle object with the same values as this one. * @returns New cloned TextStyle object */ clone() { return new _TextStyle({ align: this.align, breakWords: this.breakWords, dropShadow: this._dropShadow ? { ...this._dropShadow } : null, fill: this._fill, fontFamily: this.fontFamily, fontSize: this.fontSize, fontStyle: this.fontStyle, fontVariant: this.fontVariant, fontWeight: this.fontWeight, leading: this.leading, letterSpacing: this.letterSpacing, lineHeight: this.lineHeight, padding: this.padding, stroke: this._stroke, textBaseline: this.textBaseline, whiteSpace: this.whiteSpace, wordWrap: this.wordWrap, wordWrapWidth: this.wordWrapWidth, filters: this._filters ? [...this._filters] : void 0 }); } /** * Returns the final padding for the text style, taking into account any filters applied. * Used internally for correct measurements * @internal * @returns {number} The final padding for the text style. */ _getFinalPadding() { let filterPadding = 0; if (this._filters) { for (let i = 0; i < this._filters.length; i++) { filterPadding += this._filters[i].padding; } } return Math.max(this._padding, filterPadding); } /** * Destroys this text style. * @param options - Options parameter. A boolean will act as if all options * have been set to that value * @example * // Destroy the text style and its textures * textStyle.destroy({ texture: true, textureSource: true }); * textStyle.destroy(true); */ destroy(options = false) { this.removeAllListeners(); const destroyTexture = typeof options === "boolean" ? options : options?.texture; if (destroyTexture) { const destroyTextureSource = typeof options === "boolean" ? options : options?.textureSource; if (this._fill?.texture) { this._fill.texture.destroy(destroyTextureSource); } if (this._originalFill?.texture) { this._originalFill.texture.destroy(destroyTextureSource); } if (this._stroke?.texture) { this._stroke.texture.destroy(destroyTextureSource); } if (this._originalStroke?.texture) { this._originalStroke.texture.destroy(destroyTextureSource); } } this._fill = null; this._stroke = null; this.dropShadow = null; this._originalStroke = null; this._originalFill = null; } _createProxy(value, cb) { return new Proxy(value, { set: (target, property, newValue) => { target[property] = newValue; cb?.(property, newValue); this.update(); return true; } }); } _isFillStyle(value) { return (value ?? null) !== null && !(Color.Color.isColorLike(value) || value instanceof FillGradient.FillGradient || value instanceof FillPattern.FillPattern); } }; /** * Default drop shadow settings used when enabling drop shadows on text. * These values are used as the base configuration when drop shadows are enabled without specific settings. * @example * ```ts * // Customize default settings globally * TextStyle.defaultDropShadow.alpha = 0.5; // 50% opacity for all shadows * TextStyle.defaultDropShadow.blur = 2; // 2px blur for all shadows * TextStyle.defaultDropShadow.color = 'blue'; // Blue shadows by default * ``` */ _TextStyle.defaultDropShadow = { alpha: 1, angle: Math.PI / 6, blur: 0, color: "black", distance: 5 }; /** * Default text style settings used when creating new text objects. * These values serve as the base configuration and can be customized globally. * @example * ```ts * // Customize default text style globally * TextStyle.defaultTextStyle.fontSize = 16; * TextStyle.defaultTextStyle.fill = 0x333333; * TextStyle.defaultTextStyle.fontFamily = ['Arial', 'Helvetica', 'sans-serif']; * ``` */ _TextStyle.defaultTextStyle = { align: "left", breakWords: false, dropShadow: null, fill: "black", fontFamily: "Arial", fontSize: 26, fontStyle: "normal", fontVariant: "normal", fontWeight: "normal", leading: 0, letterSpacing: 0, lineHeight: 0, padding: 0, stroke: null, textBaseline: "alphabetic", trim: false, whiteSpace: "pre", wordWrap: false, wordWrapWidth: 100 }; let TextStyle = _TextStyle; function convertV7Tov8Style(style) { const oldStyle = style; if (typeof oldStyle.dropShadow === "boolean" && oldStyle.dropShadow) { const defaults = TextStyle.defaultDropShadow; style.dropShadow = { alpha: oldStyle.dropShadowAlpha ?? defaults.alpha, angle: oldStyle.dropShadowAngle ?? defaults.angle, blur: oldStyle.dropShadowBlur ?? defaults.blur, color: oldStyle.dropShadowColor ?? defaults.color, distance: oldStyle.dropShadowDistance ?? defaults.distance }; } if (oldStyle.strokeThickness !== void 0) { deprecation.deprecation(deprecation.v8_0_0, "strokeThickness is now a part of stroke"); const color = oldStyle.stroke; let obj = {}; if (Color.Color.isColorLike(color)) { obj.color = color; } else if (color instanceof FillGradient.FillGradient || color instanceof FillPattern.FillPattern) { obj.fill = color; } else if (Object.hasOwnProperty.call(color, "color") || Object.hasOwnProperty.call(color, "fill")) { obj = color; } else { throw new Error("Invalid stroke value."); } style.stroke = { ...obj, width: oldStyle.strokeThickness }; } if (Array.isArray(oldStyle.fillGradientStops)) { deprecation.deprecation(deprecation.v8_0_0, "gradient fill is now a fill pattern: `new FillGradient(...)`"); let fontSize; if (style.fontSize == null) { style.fontSize = TextStyle.defaultTextStyle.fontSize; } else if (typeof style.fontSize === "string") { fontSize = parseInt(style.fontSize, 10); } else { fontSize = style.fontSize; } const gradientFill = new FillGradient.FillGradient({ start: { x: 0, y: 0 }, end: { x: 0, y: (fontSize || 0) * 1.7 } }); const fills = oldStyle.fillGradientStops.map((color) => Color.Color.shared.setValue(color).toNumber()); fills.forEach((number, index) => { const ratio = index / (fills.length - 1); gradientFill.addColorStop(ratio, number); }); style.fill = { fill: gradientFill }; } } exports.TextStyle = TextStyle; //# sourceMappingURL=TextStyle.js.map