platform.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. export const DEFAULT_FONT_SIZE = 12;
  2. export const DEFAULT_FONT_FAMILY = 'sans-serif';
  3. export const DEFAULT_FONT = `${DEFAULT_FONT_SIZE}px ${DEFAULT_FONT_FAMILY}`;
  4. interface Platform {
  5. // TODO CanvasLike?
  6. createCanvas(): HTMLCanvasElement
  7. measureText(text: string, font?: string): { width: number }
  8. loadImage(
  9. src: string,
  10. onload: () => void | HTMLImageElement['onload'],
  11. onerror: () => void | HTMLImageElement['onerror']
  12. ): HTMLImageElement
  13. }
  14. // Text width map used for environment there is no canvas
  15. // Only common ascii is used for size concern.
  16. // Generated from following code
  17. //
  18. // ctx.font = '12px sans-serif';
  19. // const asciiRange = [32, 126];
  20. // let mapStr = '';
  21. // for (let i = asciiRange[0]; i <= asciiRange[1]; i++) {
  22. // const char = String.fromCharCode(i);
  23. // const width = ctx.measureText(char).width;
  24. // const ratio = Math.round(width / 12 * 100);
  25. // mapStr += String.fromCharCode(ratio + 20))
  26. // }
  27. // mapStr.replace(/\\/g, '\\\\');
  28. const OFFSET = 20;
  29. const SCALE = 100;
  30. // TODO other basic fonts?
  31. // eslint-disable-next-line
  32. const defaultWidthMapStr = `007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\WQb\\0FWLg\\bWb\\WQ\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\FFF5.5N`;
  33. function getTextWidthMap(mapStr: string): Record<string, number> {
  34. const map: Record<string, number> = {};
  35. if (typeof JSON === 'undefined') {
  36. return map;
  37. }
  38. for (let i = 0; i < mapStr.length; i++) {
  39. const char = String.fromCharCode(i + 32);
  40. const size = (mapStr.charCodeAt(i) - OFFSET) / SCALE;
  41. map[char] = size;
  42. }
  43. return map;
  44. }
  45. export const DEFAULT_TEXT_WIDTH_MAP = getTextWidthMap(defaultWidthMapStr);
  46. export const platformApi: Platform = {
  47. // Export methods
  48. createCanvas() {
  49. return typeof document !== 'undefined'
  50. && document.createElement('canvas');
  51. },
  52. measureText: (function () {
  53. let _ctx: CanvasRenderingContext2D;
  54. let _cachedFont: string;
  55. return (text: string, font?: string) => {
  56. if (!_ctx) {
  57. const canvas = platformApi.createCanvas();
  58. _ctx = canvas && canvas.getContext('2d');
  59. }
  60. if (_ctx) {
  61. if (_cachedFont !== font) {
  62. _cachedFont = _ctx.font = font || DEFAULT_FONT;
  63. }
  64. return _ctx.measureText(text);
  65. }
  66. else {
  67. text = text || '';
  68. font = font || DEFAULT_FONT;
  69. // Use font size if there is no other method can be used.
  70. const res = /((?:\d+)?\.?\d*)px/.exec(font);
  71. const fontSize = res && +res[1] || DEFAULT_FONT_SIZE;
  72. let width = 0;
  73. if (font.indexOf('mono') >= 0) { // is monospace
  74. width = fontSize * text.length;
  75. }
  76. else {
  77. for (let i = 0; i < text.length; i++) {
  78. const preCalcWidth = DEFAULT_TEXT_WIDTH_MAP[text[i]];
  79. width += preCalcWidth == null ? fontSize : (preCalcWidth * fontSize);
  80. }
  81. }
  82. return { width };
  83. }
  84. };
  85. })(),
  86. loadImage(src, onload, onerror) {
  87. const image = new Image();
  88. image.onload = onload;
  89. image.onerror = onerror;
  90. image.src = src;
  91. return image;
  92. }
  93. };
  94. export function setPlatformAPI(newPlatformApis: Partial<Platform>) {
  95. for (let key in platformApi) {
  96. // Don't assign unknown methods.
  97. if ((newPlatformApis as any)[key]) {
  98. (platformApi as any)[key] = (newPlatformApis as any)[key];
  99. }
  100. }
  101. }