Avoid try/catch, DOM and workers

This commit is contained in:
Andreas Hocevar
2020-03-21 15:49:01 +01:00
parent 6dcc54bfb8
commit 3f7f999db0
6 changed files with 85 additions and 100 deletions
+46 -33
View File
@@ -4,10 +4,13 @@
/** /**
* @typedef {Object} FontParameters * @typedef {Object} FontParameters
* @property {Array<string>} families
* @property {string} style * @property {string} style
* @property {string} variant
* @property {string} weight * @property {string} weight
* @property {string} size
* @property {string} lineHeight * @property {string} lineHeight
* @property {string} family
* @property {Array<string>} families
*/ */
@@ -65,42 +68,52 @@ export const CLASS_CONTROL = 'ol-control';
*/ */
export const CLASS_COLLAPSED = 'ol-collapsed'; export const CLASS_COLLAPSED = 'ol-collapsed';
/**
* From http://stackoverflow.com/questions/10135697/regex-to-parse-any-css-font
* @type {RegExp}
*/
const fontRegEx = new RegExp([
'^\\s*(?=(?:(?:[-a-z]+\\s*){0,2}(italic|oblique))?)',
'(?=(?:(?:[-a-z]+\\s*){0,2}(small-caps))?)',
'(?=(?:(?:[-a-z]+\\s*){0,2}(bold(?:er)?|lighter|[1-9]00 ))?)',
'(?:(?:normal|\\1|\\2|\\3)\\s*){0,3}((?:xx?-)?',
'(?:small|large)|medium|smaller|larger|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx]))',
'(?:\\s*\\/\\s*(normal|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx])?))',
'?\\s*([-,\\"\\\'\\sa-z]+?)\\s*$'
].join(''), 'i');
const fontRegExMatchIndex = [
'style',
'variant',
'weight',
'size',
'lineHeight',
'family'
];
/** /**
* Get the list of font families from a font spec. Note that this doesn't work * Get the list of font families from a font spec. Note that this doesn't work
* for font families that have commas in them. * for font families that have commas in them.
* @param {string} fontSpec The CSS font property. * @param {string} fontSpec The CSS font property.
* @param {function(FontParameters):void} callback Called with the font families * @return {FontParameters} The font parameters (or null if the input spec is invalid).
* (or null if the input spec is invalid).
*/ */
export const getFontParameters = (function() { export const getFontParameters = function(fontSpec) {
/** const match = fontSpec.match(fontRegEx);
* @type {CSSStyleDeclaration} if (!match) {
*/ return null;
let style; }
/** const style = /** @type {FontParameters} */ ({
* @type {Object<string, FontParameters>} lineHeight: 'normal',
*/ size: '1.2em',
const cache = {}; style: 'normal',
return function(fontSpec, callback) { weight: 'normal',
if (!style) { variant: 'normal'
style = document.createElement('div').style; });
for (let i = 0, ii = fontRegExMatchIndex.length; i < ii; ++i) {
const value = match[i + 1];
if (value !== undefined) {
style[fontRegExMatchIndex[i]] = value;
} }
if (!(fontSpec in cache)) { }
style.font = fontSpec; style.families = style.family.split(/,\s?/);
const family = style.fontFamily; return style;
if (!family) { };
callback(null);
}
const families = family.split(/,\s?/);
cache[fontSpec] = {
families: families,
weight: style.fontWeight,
style: style.fontStyle,
lineHeight: style.lineHeight
};
style.font = '';
}
callback(cache[fontSpec]);
};
})();
+5 -3
View File
@@ -1,3 +1,5 @@
import {WORKER_OFFSCREEN_CANVAS} from './has.js';
/** /**
* @module ol/dom * @module ol/dom
*/ */
@@ -14,9 +16,9 @@
export function createCanvasContext2D(opt_width, opt_height, opt_canvasPool) { export function createCanvasContext2D(opt_width, opt_height, opt_canvasPool) {
const canvas = opt_canvasPool && opt_canvasPool.length ? const canvas = opt_canvasPool && opt_canvasPool.length ?
opt_canvasPool.shift() : opt_canvasPool.shift() :
'document' in self ? WORKER_OFFSCREEN_CANVAS ?
document.createElement('canvas') : new OffscreenCanvas(opt_width || 300, opt_height || 300) :
new OffscreenCanvas(opt_width || 300, opt_height || 300); document.createElement('canvas');
if (opt_width) { if (opt_width) {
canvas.width = opt_width; canvas.width = opt_width;
} }
+4 -16
View File
@@ -37,27 +37,15 @@ export const MAC = ua.indexOf('macintosh') !== -1;
* @type {number} * @type {number}
* @api * @api
*/ */
export const DEVICE_PIXEL_RATIO = (function() { export const DEVICE_PIXEL_RATIO = (typeof self !== 'undefined' ? self.devicePixelRatio : window.devicePixelRatio) || 1;
try {
return self.devicePixelRatio;
} catch (e) {
return window.devicePixelRatio || 1;
}
})();
/** /**
* The execution context is a window. * The execution context is a worker with OffscreenCanvas available.
* @const * @const
* @type {boolean} * @type {boolean}
*/ */
export const WINDOW = (function() { export const WORKER_OFFSCREEN_CANVAS = typeof WorkerGlobalScope !== 'undefined' && typeof OffscreenCanvas !== 'undefined' &&
try { self instanceof WorkerGlobalScope; //eslint-disable-line
return 'document' in self;
} catch (e) {
// ancient browsers don't have `self`
return true;
}
})();
/** /**
* Image.prototype.decode() is supported. * Image.prototype.decode() is supported.
+27 -45
View File
@@ -6,7 +6,7 @@ import {createCanvasContext2D} from '../dom.js';
import {clear} from '../obj.js'; import {clear} from '../obj.js';
import BaseObject from '../Object.js'; import BaseObject from '../Object.js';
import EventTarget from '../events/Target.js'; import EventTarget from '../events/Target.js';
import {WINDOW} from '../has.js'; import {WORKER_OFFSCREEN_CANVAS} from '../has.js';
import {toString} from '../transform.js'; import {toString} from '../transform.js';
@@ -266,7 +266,8 @@ export const registerFont = (function() {
} }
} }
function fontCallback(font) { return function(fontSpec) {
const font = getFontParameters(fontSpec);
if (!font) { if (!font) {
return; return;
} }
@@ -284,31 +285,6 @@ export const registerFont = (function() {
} }
} }
} }
}
return function(fontSpec) {
if (WINDOW) {
getFontParameters(fontSpec, fontCallback);
} else if (self.postMessage) {
/** @type {any} */
const worker = self;
worker.postMessage({
action: 'getFontParameters',
font: fontSpec
});
worker.addEventListener('message', function handler(event) {
if (event.data.action === 'getFontParameters') {
worker.removeEventListener('message', handler);
const font = event.data.font;
fontCallback(font);
if (!textHeights[fontSpec]) {
const metrics = measureText(fontSpec, 'Žg');
const lineHeight = isNaN(font.lineHeight) ? 1.2 : Number(font.lineHeight);
textHeights[fontSpec] = lineHeight * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
}
}
});
}
}; };
})(); })();
@@ -323,22 +299,29 @@ export const measureTextHeight = (function() {
*/ */
let div; let div;
const heights = textHeights; const heights = textHeights;
return function(font) { return function(fontSpec) {
let height = heights[font]; let height = heights[fontSpec];
if (height == undefined) { if (height == undefined) {
if (!div) { if (WORKER_OFFSCREEN_CANVAS) {
div = document.createElement('div'); const font = getFontParameters(fontSpec);
div.innerHTML = 'M'; const metrics = measureText(fontSpec, 'Žg');
div.style.margin = '0 !important'; const lineHeight = isNaN(Number(font.lineHeight)) ? 1.2 : Number(font.lineHeight);
div.style.padding = '0 !important'; textHeights[fontSpec] = lineHeight * (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent);
div.style.position = 'absolute !important'; } else {
div.style.left = '-99999px !important'; if (!div) {
div = document.createElement('div');
div.innerHTML = 'M';
div.style.margin = '0 !important';
div.style.padding = '0 !important';
div.style.position = 'absolute !important';
div.style.left = '-99999px !important';
}
div.style.font = fontSpec;
document.body.appendChild(div);
height = div.offsetHeight;
heights[fontSpec] = height;
document.body.removeChild(div);
} }
div.style.font = font;
document.body.appendChild(div);
height = div.offsetHeight;
heights[font] = height;
document.body.removeChild(div);
} }
return height; return height;
}; };
@@ -369,7 +352,6 @@ export function measureTextWidth(font, text) {
return measureText(font, text).width; return measureText(font, text).width;
} }
/** /**
* Measure text width using a cache. * Measure text width using a cache.
* @param {string} font The font. * @param {string} font The font.
@@ -484,13 +466,13 @@ let createTransformStringCanvas = null;
* @return {string} CSS transform. * @return {string} CSS transform.
*/ */
export function createTransformString(transform) { export function createTransformString(transform) {
if (WINDOW) { if (WORKER_OFFSCREEN_CANVAS) {
return toString(transform);
} else {
if (!createTransformStringCanvas) { if (!createTransformStringCanvas) {
createTransformStringCanvas = createCanvasContext2D(1, 1).canvas; createTransformStringCanvas = createCanvasContext2D(1, 1).canvas;
} }
createTransformStringCanvas.style.transform = toString(transform); createTransformStringCanvas.style.transform = toString(transform);
return createTransformStringCanvas.style.transform; return createTransformStringCanvas.style.transform;
} else {
return toString(transform);
} }
} }
+2 -2
View File
@@ -18,7 +18,7 @@ import {
} from '../../transform.js'; } from '../../transform.js';
import {defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js'; import {defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
import RBush from 'rbush/rbush.js'; import RBush from 'rbush/rbush.js';
import {WINDOW} from '../../has.js'; import {WORKER_OFFSCREEN_CANVAS} from '../../has.js';
/** /**
@@ -206,7 +206,7 @@ class Executor {
contextInstructions.push('lineJoin', strokeState.lineJoin); contextInstructions.push('lineJoin', strokeState.lineJoin);
contextInstructions.push('miterLimit', strokeState.miterLimit); contextInstructions.push('miterLimit', strokeState.miterLimit);
// eslint-disable-next-line // eslint-disable-next-line
const Context = WINDOW ? CanvasRenderingContext2D : OffscreenCanvasRenderingContext2D; const Context = WORKER_OFFSCREEN_CANVAS ? OffscreenCanvasRenderingContext2D : CanvasRenderingContext2D;
if (Context.prototype.setLineDash) { if (Context.prototype.setLineDash) {
contextInstructions.push('setLineDash', [strokeState.lineDash]); contextInstructions.push('setLineDash', [strokeState.lineDash]);
contextInstructions.push('lineDashOffset', strokeState.lineDashOffset); contextInstructions.push('lineDashOffset', strokeState.lineDashOffset);
+1 -1
View File
@@ -3,7 +3,7 @@
/* Basic Options */ /* Basic Options */
"target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ "target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es2017", "dom"], /* Specify library files to be included in the compilation. */ "lib": ["es2017", "dom", "webworker"], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */ "allowJs": true, /* Allow javascript files to be compiled. */
"checkJs": true, /* Report errors in .js files. */ "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */