277 lines
9.4 KiB
JavaScript
277 lines
9.4 KiB
JavaScript
|
|
import { settings, utils } from "@pixi/core";
|
||
|
|
import { TextStyle } from "@pixi/text";
|
||
|
|
const _HTMLTextStyle = class _HTMLTextStyle2 extends TextStyle {
|
||
|
|
constructor() {
|
||
|
|
super(...arguments), this._fonts = [], this._overrides = [], this._stylesheet = "", this.fontsDirty = !1;
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Convert a TextStyle to HTMLTextStyle
|
||
|
|
* @param originalStyle
|
||
|
|
* @example
|
||
|
|
* import {TextStyle } from 'pixi.js';
|
||
|
|
* import {HTMLTextStyle} from '@pixi/text-html';
|
||
|
|
* const style = new TextStyle();
|
||
|
|
* const htmlStyle = HTMLTextStyle.from(style);
|
||
|
|
*/
|
||
|
|
static from(originalStyle) {
|
||
|
|
return new _HTMLTextStyle2(
|
||
|
|
Object.keys(_HTMLTextStyle2.defaultOptions).reduce((obj, prop) => ({ ...obj, [prop]: originalStyle[prop] }), {})
|
||
|
|
);
|
||
|
|
}
|
||
|
|
/** Clear the current font */
|
||
|
|
cleanFonts() {
|
||
|
|
this._fonts.length > 0 && (this._fonts.forEach((font) => {
|
||
|
|
URL.revokeObjectURL(font.src), font.refs--, font.refs === 0 && (font.fontFace && document.fonts.delete(font.fontFace), delete _HTMLTextStyle2.availableFonts[font.originalUrl]);
|
||
|
|
}), this.fontFamily = "Arial", this._fonts.length = 0, this.styleID++, this.fontsDirty = !0);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Because of how HTMLText renders, fonts need to be imported
|
||
|
|
* @param url
|
||
|
|
* @param options
|
||
|
|
*/
|
||
|
|
loadFont(url, options = {}) {
|
||
|
|
const { availableFonts } = _HTMLTextStyle2;
|
||
|
|
if (availableFonts[url]) {
|
||
|
|
const font = availableFonts[url];
|
||
|
|
return this._fonts.push(font), font.refs++, this.styleID++, this.fontsDirty = !0, Promise.resolve();
|
||
|
|
}
|
||
|
|
return settings.ADAPTER.fetch(url).then((response) => response.blob()).then(async (blob) => new Promise((resolve, reject) => {
|
||
|
|
const src = URL.createObjectURL(blob), reader = new FileReader();
|
||
|
|
reader.onload = () => resolve([src, reader.result]), reader.onerror = reject, reader.readAsDataURL(blob);
|
||
|
|
})).then(async ([src, dataSrc]) => {
|
||
|
|
const font = Object.assign({
|
||
|
|
family: utils.path.basename(url, utils.path.extname(url)),
|
||
|
|
weight: "normal",
|
||
|
|
style: "normal",
|
||
|
|
display: "auto",
|
||
|
|
src,
|
||
|
|
dataSrc,
|
||
|
|
refs: 1,
|
||
|
|
originalUrl: url,
|
||
|
|
fontFace: null
|
||
|
|
}, options);
|
||
|
|
availableFonts[url] = font, this._fonts.push(font), this.styleID++;
|
||
|
|
const fontFace = new FontFace(font.family, `url(${font.src})`, {
|
||
|
|
weight: font.weight,
|
||
|
|
style: font.style,
|
||
|
|
display: font.display
|
||
|
|
});
|
||
|
|
font.fontFace = fontFace, await fontFace.load(), document.fonts.add(fontFace), await document.fonts.ready, this.styleID++, this.fontsDirty = !0;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Add a style override, this can be any CSS property
|
||
|
|
* it will override any built-in style. This is the
|
||
|
|
* property and the value as a string (e.g., `color: red`).
|
||
|
|
* This will override any other internal style.
|
||
|
|
* @param {string} value - CSS style(s) to add.
|
||
|
|
* @example
|
||
|
|
* style.addOverride('background-color: red');
|
||
|
|
*/
|
||
|
|
addOverride(...value) {
|
||
|
|
const toAdd = value.filter((v) => !this._overrides.includes(v));
|
||
|
|
toAdd.length > 0 && (this._overrides.push(...toAdd), this.styleID++);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Remove any overrides that match the value.
|
||
|
|
* @param {string} value - CSS style to remove.
|
||
|
|
* @example
|
||
|
|
* style.removeOverride('background-color: red');
|
||
|
|
*/
|
||
|
|
removeOverride(...value) {
|
||
|
|
const toRemove = value.filter((v) => this._overrides.includes(v));
|
||
|
|
toRemove.length > 0 && (this._overrides = this._overrides.filter((v) => !toRemove.includes(v)), this.styleID++);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Internally converts all of the style properties into CSS equivalents.
|
||
|
|
* @param scale
|
||
|
|
* @returns The CSS style string, for setting `style` property of root HTMLElement.
|
||
|
|
*/
|
||
|
|
toCSS(scale) {
|
||
|
|
return [
|
||
|
|
`transform: scale(${scale})`,
|
||
|
|
"transform-origin: top left",
|
||
|
|
"display: inline-block",
|
||
|
|
`color: ${this.normalizeColor(this.fill)}`,
|
||
|
|
`font-size: ${this.fontSize}px`,
|
||
|
|
`font-family: ${this.fontFamily}`,
|
||
|
|
`font-weight: ${this.fontWeight}`,
|
||
|
|
`font-style: ${this.fontStyle}`,
|
||
|
|
`font-variant: ${this.fontVariant}`,
|
||
|
|
`letter-spacing: ${this.letterSpacing}px`,
|
||
|
|
`text-align: ${this.align}`,
|
||
|
|
`padding: ${this.padding}px`,
|
||
|
|
`white-space: ${this.whiteSpace}`,
|
||
|
|
...this.lineHeight ? [`line-height: ${this.lineHeight}px`] : [],
|
||
|
|
...this.wordWrap ? [
|
||
|
|
`word-wrap: ${this.breakWords ? "break-all" : "break-word"}`,
|
||
|
|
`max-width: ${this.wordWrapWidth}px`
|
||
|
|
] : [],
|
||
|
|
...this.strokeThickness ? [
|
||
|
|
`-webkit-text-stroke-width: ${this.strokeThickness}px`,
|
||
|
|
`-webkit-text-stroke-color: ${this.normalizeColor(this.stroke)}`,
|
||
|
|
`text-stroke-width: ${this.strokeThickness}px`,
|
||
|
|
`text-stroke-color: ${this.normalizeColor(this.stroke)}`,
|
||
|
|
"paint-order: stroke"
|
||
|
|
] : [],
|
||
|
|
...this.dropShadow ? [this.dropShadowToCSS()] : [],
|
||
|
|
...this._overrides
|
||
|
|
].join(";");
|
||
|
|
}
|
||
|
|
/** Get the font CSS styles from the loaded font, If available. */
|
||
|
|
toGlobalCSS() {
|
||
|
|
return this._fonts.reduce((result, font) => `${result}
|
||
|
|
@font-face {
|
||
|
|
font-family: "${font.family}";
|
||
|
|
src: url('${font.dataSrc}');
|
||
|
|
font-weight: ${font.weight};
|
||
|
|
font-style: ${font.style};
|
||
|
|
font-display: ${font.display};
|
||
|
|
}`, this._stylesheet);
|
||
|
|
}
|
||
|
|
/** Internal stylesheet contents, useful for creating rules for rendering */
|
||
|
|
get stylesheet() {
|
||
|
|
return this._stylesheet;
|
||
|
|
}
|
||
|
|
set stylesheet(value) {
|
||
|
|
this._stylesheet !== value && (this._stylesheet = value, this.styleID++);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Convert numerical colors into hex-strings
|
||
|
|
* @param color
|
||
|
|
*/
|
||
|
|
normalizeColor(color) {
|
||
|
|
return Array.isArray(color) && (color = utils.rgb2hex(color)), typeof color == "number" ? utils.hex2string(color) : color;
|
||
|
|
}
|
||
|
|
/** Convert the internal drop-shadow settings to CSS text-shadow */
|
||
|
|
dropShadowToCSS() {
|
||
|
|
let color = this.normalizeColor(this.dropShadowColor);
|
||
|
|
const alpha = this.dropShadowAlpha, x = Math.round(Math.cos(this.dropShadowAngle) * this.dropShadowDistance), y = Math.round(Math.sin(this.dropShadowAngle) * this.dropShadowDistance);
|
||
|
|
color.startsWith("#") && alpha < 1 && (color += (alpha * 255 | 0).toString(16).padStart(2, "0"));
|
||
|
|
const position = `${x}px ${y}px`;
|
||
|
|
return this.dropShadowBlur > 0 ? `text-shadow: ${position} ${this.dropShadowBlur}px ${color}` : `text-shadow: ${position} ${color}`;
|
||
|
|
}
|
||
|
|
/** Resets all properties to the defaults specified in TextStyle.prototype._default */
|
||
|
|
reset() {
|
||
|
|
Object.assign(this, _HTMLTextStyle2.defaultOptions);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Called after the image is loaded but before drawing to the canvas.
|
||
|
|
* Mostly used to handle Safari's font loading bug.
|
||
|
|
* @ignore
|
||
|
|
*/
|
||
|
|
onBeforeDraw() {
|
||
|
|
const { fontsDirty: prevFontsDirty } = this;
|
||
|
|
return this.fontsDirty = !1, this.isSafari && this._fonts.length > 0 && prevFontsDirty ? new Promise((resolve) => setTimeout(resolve, 100)) : Promise.resolve();
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Proving that Safari is the new IE
|
||
|
|
* @ignore
|
||
|
|
*/
|
||
|
|
get isSafari() {
|
||
|
|
const { userAgent } = settings.ADAPTER.getNavigator();
|
||
|
|
return /^((?!chrome|android).)*safari/i.test(userAgent);
|
||
|
|
}
|
||
|
|
set fillGradientStops(_value) {
|
||
|
|
console.warn("[HTMLTextStyle] fillGradientStops is not supported by HTMLText");
|
||
|
|
}
|
||
|
|
get fillGradientStops() {
|
||
|
|
return super.fillGradientStops;
|
||
|
|
}
|
||
|
|
set fillGradientType(_value) {
|
||
|
|
console.warn("[HTMLTextStyle] fillGradientType is not supported by HTMLText");
|
||
|
|
}
|
||
|
|
get fillGradientType() {
|
||
|
|
return super.fillGradientType;
|
||
|
|
}
|
||
|
|
set miterLimit(_value) {
|
||
|
|
console.warn("[HTMLTextStyle] miterLimit is not supported by HTMLText");
|
||
|
|
}
|
||
|
|
get miterLimit() {
|
||
|
|
return super.miterLimit;
|
||
|
|
}
|
||
|
|
set trim(_value) {
|
||
|
|
console.warn("[HTMLTextStyle] trim is not supported by HTMLText");
|
||
|
|
}
|
||
|
|
get trim() {
|
||
|
|
return super.trim;
|
||
|
|
}
|
||
|
|
set textBaseline(_value) {
|
||
|
|
console.warn("[HTMLTextStyle] textBaseline is not supported by HTMLText");
|
||
|
|
}
|
||
|
|
get textBaseline() {
|
||
|
|
return super.textBaseline;
|
||
|
|
}
|
||
|
|
set leading(_value) {
|
||
|
|
console.warn("[HTMLTextStyle] leading is not supported by HTMLText");
|
||
|
|
}
|
||
|
|
get leading() {
|
||
|
|
return super.leading;
|
||
|
|
}
|
||
|
|
set lineJoin(_value) {
|
||
|
|
console.warn("[HTMLTextStyle] lineJoin is not supported by HTMLText");
|
||
|
|
}
|
||
|
|
get lineJoin() {
|
||
|
|
return super.lineJoin;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
_HTMLTextStyle.availableFonts = {}, /**
|
||
|
|
* List of default options, these are largely the same as TextStyle,
|
||
|
|
* with the exception of whiteSpace, which is set to 'normal' by default.
|
||
|
|
*/
|
||
|
|
_HTMLTextStyle.defaultOptions = {
|
||
|
|
/** Align */
|
||
|
|
align: "left",
|
||
|
|
/** Break words */
|
||
|
|
breakWords: !1,
|
||
|
|
/** Drop shadow */
|
||
|
|
dropShadow: !1,
|
||
|
|
/** Drop shadow alpha */
|
||
|
|
dropShadowAlpha: 1,
|
||
|
|
/**
|
||
|
|
* Drop shadow angle
|
||
|
|
* @type {number}
|
||
|
|
* @default Math.PI / 6
|
||
|
|
*/
|
||
|
|
dropShadowAngle: Math.PI / 6,
|
||
|
|
/** Drop shadow blur */
|
||
|
|
dropShadowBlur: 0,
|
||
|
|
/** Drop shadow color */
|
||
|
|
dropShadowColor: "black",
|
||
|
|
/** Drop shadow distance */
|
||
|
|
dropShadowDistance: 5,
|
||
|
|
/** Fill */
|
||
|
|
fill: "black",
|
||
|
|
/** Font family */
|
||
|
|
fontFamily: "Arial",
|
||
|
|
/** Font size */
|
||
|
|
fontSize: 26,
|
||
|
|
/** Font style */
|
||
|
|
fontStyle: "normal",
|
||
|
|
/** Font variant */
|
||
|
|
fontVariant: "normal",
|
||
|
|
/** Font weight */
|
||
|
|
fontWeight: "normal",
|
||
|
|
/** Letter spacing */
|
||
|
|
letterSpacing: 0,
|
||
|
|
/** Line height */
|
||
|
|
lineHeight: 0,
|
||
|
|
/** Padding */
|
||
|
|
padding: 0,
|
||
|
|
/** Stroke */
|
||
|
|
stroke: "black",
|
||
|
|
/** Stroke thickness */
|
||
|
|
strokeThickness: 0,
|
||
|
|
/** White space */
|
||
|
|
whiteSpace: "normal",
|
||
|
|
/** Word wrap */
|
||
|
|
wordWrap: !1,
|
||
|
|
/** Word wrap width */
|
||
|
|
wordWrapWidth: 100
|
||
|
|
};
|
||
|
|
let HTMLTextStyle = _HTMLTextStyle;
|
||
|
|
export {
|
||
|
|
HTMLTextStyle
|
||
|
|
};
|
||
|
|
//# sourceMappingURL=HTMLTextStyle.mjs.map
|