UNPKG

lazy-widgets

Version:

Typescript retained mode GUI for the HTML canvas API

96 lines 3.44 kB
import { Msg } from "../core/Strings.js"; /** The rendering context used for measuring text. */ let measureContext = null; /** The default font style. */ let defaultFontStyle = ''; /** * The LFU cache of measured text. Contains a limited amount of text * measurements ordered by most to least frequently used. */ const measureCache = []; /** The size limit for the LFU cache (`measureCache`) */ const measureCacheLimit = 64; /** * The minimum amount of time since the last hit for a cache entry to be * considered as expired, in milliseconds. */ const expiryThreshold = 10000; /** * Measures the dimensions of a given string of text with a given font. * * Note that the first time calling this function is slower than subsequent * calls because a dedicated canvas context must be created. * * @returns Returns a the TextMetrics of the measured text. * * @category Helper */ export function measureTextDims(text, font) { const measureTime = (new Date()).getTime(); // Get cached value let cacheHit = null; let cacheHitIdx = -1; let removalIdx = measureCache.length - 1; for (let i = 0; i < measureCache.length; i++) { const cacheVal = measureCache[i]; if (cacheVal[0] === font && cacheVal[1] === text) { cacheHit = cacheVal; cacheHitIdx = i; break; } // Mark last expired cache entry for removal. If none is found, the last // entry in the cache (even if not expired) is marked for removal. if (measureTime - cacheVal[4] > expiryThreshold) { removalIdx = i; } } // If there was a cache hit, increment hits, update last hit time, bump in // cache and return cached metrics if (cacheHit) { const newFreq = ++cacheHit[3]; cacheHit[4] = measureTime; if (cacheHitIdx > 0) { let candidateIdx = 0; for (; candidateIdx < cacheHitIdx; candidateIdx++) { if (measureCache[candidateIdx][3] <= newFreq) { break; } } if (candidateIdx !== cacheHitIdx) { measureCache.splice(cacheHitIdx, 1); measureCache.splice(candidateIdx, 0, cacheHit); } } return cacheHit[2]; } // Get canvas context if not yet got if (measureContext === null) { const tempCanvas = document.createElement('canvas'); measureContext = tempCanvas.getContext('2d'); if (measureContext === null) { throw new Error(Msg.CANVAS_CONTEXT); } measureContext.fontKerning = 'normal'; defaultFontStyle = measureContext.font; } // Set font measureContext.font = font === '' ? defaultFontStyle : font; // Measure text const metrics = measureContext.measureText(text); // Cache metrics. Remove least frequently used text entry if cache size // exceeded. if (measureCacheLimit > 0) { if (measureCache.length === measureCacheLimit) { measureCache.splice(removalIdx, 1); } let candidateIdx = 0; for (; candidateIdx < measureCache.length; candidateIdx++) { if (measureCache[candidateIdx][3] <= 1) { break; } } measureCache.splice(candidateIdx, 0, [font, text, metrics, 1, measureTime]); } return metrics; } //# sourceMappingURL=measureTextDims.js.map