export const DEFAULT_FONT_SIZE = 12;
|
export const DEFAULT_FONT_FAMILY = 'sans-serif';
|
export const DEFAULT_FONT = `${DEFAULT_FONT_SIZE}px ${DEFAULT_FONT_FAMILY}`;
|
|
interface Platform {
|
// TODO CanvasLike?
|
createCanvas(): HTMLCanvasElement
|
measureText(text: string, font?: string): { width: number }
|
loadImage(
|
src: string,
|
onload: () => void | HTMLImageElement['onload'],
|
onerror: () => void | HTMLImageElement['onerror']
|
): HTMLImageElement
|
}
|
|
// Text width map used for environment there is no canvas
|
// Only common ascii is used for size concern.
|
|
// Generated from following code
|
//
|
// ctx.font = '12px sans-serif';
|
// const asciiRange = [32, 126];
|
// let mapStr = '';
|
// for (let i = asciiRange[0]; i <= asciiRange[1]; i++) {
|
// const char = String.fromCharCode(i);
|
// const width = ctx.measureText(char).width;
|
// const ratio = Math.round(width / 12 * 100);
|
// mapStr += String.fromCharCode(ratio + 20))
|
// }
|
// mapStr.replace(/\\/g, '\\\\');
|
const OFFSET = 20;
|
const SCALE = 100;
|
// TODO other basic fonts?
|
// eslint-disable-next-line
|
const defaultWidthMapStr = `007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\WQb\\0FWLg\\bWb\\WQ\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\FFF5.5N`;
|
|
function getTextWidthMap(mapStr: string): Record<string, number> {
|
const map: Record<string, number> = {};
|
if (typeof JSON === 'undefined') {
|
return map;
|
}
|
for (let i = 0; i < mapStr.length; i++) {
|
const char = String.fromCharCode(i + 32);
|
const size = (mapStr.charCodeAt(i) - OFFSET) / SCALE;
|
map[char] = size;
|
}
|
return map;
|
}
|
|
export const DEFAULT_TEXT_WIDTH_MAP = getTextWidthMap(defaultWidthMapStr);
|
|
export const platformApi: Platform = {
|
// Export methods
|
createCanvas() {
|
return typeof document !== 'undefined'
|
&& document.createElement('canvas');
|
},
|
|
measureText: (function () {
|
|
let _ctx: CanvasRenderingContext2D;
|
let _cachedFont: string;
|
return (text: string, font?: string) => {
|
if (!_ctx) {
|
const canvas = platformApi.createCanvas();
|
_ctx = canvas && canvas.getContext('2d');
|
}
|
if (_ctx) {
|
if (_cachedFont !== font) {
|
_cachedFont = _ctx.font = font || DEFAULT_FONT;
|
}
|
return _ctx.measureText(text);
|
}
|
else {
|
text = text || '';
|
font = font || DEFAULT_FONT;
|
// Use font size if there is no other method can be used.
|
const res = /(\d+)px/.exec(font);
|
const fontSize = res && +res[1] || DEFAULT_FONT_SIZE;
|
let width = 0;
|
if (font.indexOf('mono') >= 0) { // is monospace
|
width = fontSize * text.length;
|
}
|
else {
|
for (let i = 0; i < text.length; i++) {
|
const preCalcWidth = DEFAULT_TEXT_WIDTH_MAP[text[i]];
|
width += preCalcWidth == null ? fontSize : (preCalcWidth * fontSize);
|
}
|
}
|
return { width };
|
}
|
};
|
})(),
|
|
loadImage(src, onload, onerror) {
|
const image = new Image();
|
image.onload = onload;
|
image.onerror = onerror;
|
image.src = src;
|
return image;
|
}
|
};
|
|
export function setPlatformAPI(newPlatformApis: Partial<Platform>) {
|
for (let key in platformApi) {
|
// Don't assign unknown methods.
|
if ((newPlatformApis as any)[key]) {
|
(platformApi as any)[key] = (newPlatformApis as any)[key];
|
}
|
}
|
}
|