lazy-widgets
Version:
Typescript retained mode GUI for the HTML canvas API
64 lines • 3.09 kB
JavaScript
import { Msg } from '../core/Strings.js';
import { AsyncMedia } from './AsyncMedia.js';
import { BackingMediaEventType } from './BackingMediaEventType.js';
import { incrementUint31 } from './incrementUint31.js';
import { measureTextDims } from './measureTextDims.js';
/**
* Renders text as an ImageBitmap, which can then be used in widgets that
* consume a {@link BackingMediaSource}, like {@link Icon}. Useful for using
* font icons instead of images.
*
* Height is retreived by measuring the fontBoundingBoxAscent and
* fontBoundingBoxDescent (falling back to actualBoundingBoxAscent and
* actualBoundingBoxDescent), as well as the hangingBaseline (falling back to
* the actualBoundingBoxAscent of the `M` character). Width is measured from the
* text being rendered, however, it's set to be the same as the height if it's
* smaller than the height to avoid issues with thin font icons (such as
* vertical ellipsis). The font is assumed to already be loaded by the time this
* class is instantiated.
*
* Throws if a scratch canvas can't be created.
*/
export class TextMedia extends AsyncMedia {
constructor(text, font, fillStyle, options) {
var _a, _b, _c, _d;
super();
this.text = text;
this.font = font;
this.fillStyle = fillStyle;
this._bitmap = null;
this._presentationHash = 0;
const metrics = measureTextDims(text, font);
const ascent = (_a = metrics.fontBoundingBoxAscent) !== null && _a !== void 0 ? _a : metrics.actualBoundingBoxAscent;
const descent = (_b = metrics.fontBoundingBoxDescent) !== null && _b !== void 0 ? _b : metrics.actualBoundingBoxDescent;
const hangingBaseline = (_c = metrics.hangingBaseline) !== null && _c !== void 0 ? _c : measureTextDims('M', font).actualBoundingBoxAscent;
const pad = Math.ceil(Math.max(ascent - hangingBaseline, descent));
this.height = Math.ceil(hangingBaseline + pad * 2);
this.width = Math.max(Math.ceil(metrics.width), this.height);
this.resolution = (_d = options === null || options === void 0 ? void 0 : options.resolution) !== null && _d !== void 0 ? _d : 1;
const canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
const context = canvas.getContext('2d');
if (!context) {
throw new Error(Msg.CANVAS_CONTEXT);
}
context.textAlign = 'center';
context.font = font;
context.fillStyle = fillStyle;
context.fillText(text, Math.trunc(this.width * 0.5), this.height - pad);
createImageBitmap(canvas).then((bitmap) => {
this._presentationHash = incrementUint31(this._presentationHash);
this._bitmap = bitmap;
this.dispatchEvent(BackingMediaEventType.Loaded);
this.dispatchEvent(BackingMediaEventType.Dirty);
});
}
get currentFrame() {
return this._bitmap;
}
get presentationHash() {
return this._presentationHash;
}
}
//# sourceMappingURL=TextMedia.js.map