context.measureText() vs method

Benchmark created on


Preparation HTML

<div id="benchmark-container">
  <canvas id="benchmark-canvas" width="800" height="150"></canvas>
</div>

Setup

const canvas = document.getElementById('benchmark-canvas');
const context = canvas.getContext('2d');
context.font = font = '16px Arial';

function generateString(length) {
  return 'x'.repeat(length);
}

const testStrings = {
  tiny: generateString(10),
  small: generateString(100),
  medium: generateString(1000),
  large: generateString(10000),
  huge: generateString(100000)
};

const allCharWidths = new Map();
const canvasGetTextWidth = (text) => {
	const width = context.measureText(text).width
	//console.log(width)
}

const getTextWidth = (text) => {
	    if (text.length === 0) return 0;

    if (!allCharWidths.has(font)) {
      allCharWidths.set(font, new Map());
    }
    const charWidths = allCharWidths.get(font);
    context.font = font;

    let result = 0;
    for (let i = 0; i < text.length; i += 1) {
      const char = text[i];
      const nextChar = i < text.length - 1 ? text[i + 1] : undefined;

      if (!charWidths.has(char)) {
        charWidths.set(char, context.measureText(char).width);
      }

      if (nextChar !== undefined) {
        if (!charWidths.has(nextChar)) {
          charWidths.set(nextChar, context.measureText(nextChar).width);
        }

        const pair = char + nextChar;
        const isFirstPair = i === 0;

        let add = 0;
        if (charWidths.has(pair)) {
          add = isFirstPair
            ? charWidths.get(pair)
            : charWidths.get(pair) - charWidths.get(char);
        } else {
          const textMetrics = context.measureText(pair);
          const { width } = textMetrics;
          charWidths.set(pair, width);
          add = isFirstPair ? width : width - charWidths.get(char);
        }
        result += add;
      } else if (result === 0) {
        result += charWidths.get(char);
      }
    }
    
    //console.log(result)
}

Test runner

Ready to run.

Testing in
TestOps/sec
len: 10 canvas
canvasGetTextWidth(testStrings.tiny)
ready
len: 10 custom
getTextWidth(testStrings.tiny)
ready
len: 100 canvas
canvasGetTextWidth(testStrings.small)
ready
len: 100 custom
getTextWidth(testStrings.small)
ready
len: 1000 canvas
canvasGetTextWidth(testStrings.medium)
ready
len: 1000 custom
getTextWidth(testStrings.medium)
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.