Определить ширину строки в HTML5 canvas

Я рисую текст на холсте HTML5 поверх изображения (генератор мемов). Я хочу рассчитать размер шрифта, чтобы строка охватывала всю ширину холста.

Итак, в принципе, есть ли способ в JavaScript определить, какой размер шрифта я должен использовать для некоторой строки определенного шрифта для заданной ширины?

2 ответов


на fillText() метод имеет необязательный четвертый аргумент, который является максимальная ширина для вывода строки.

в документации MDN говорится...

значение maxwidth

опционально; максимальная ширина для рисования. Если указано, и строка рассчитанный как более широкий, чем эта ширина, шрифт настраивается для использования более горизонтально сжатый шрифт (если он доступен или если разумно читаемый может быть синтезирован путем масштабирования текущего шрифта горизонтально) или шрифт поменьше.

однако на момент написания статьи этот аргумент не поддерживается хорошо кросс-браузером, что приводит ко второму решению с использованием measureText() чтобы определить размеры строки без ее рендеринга.

var width = ctx.measureText(text).width;

вот как я могу это сделать...

var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d');

// Font sizes must be in descending order. You may need to use `sort()`
// if you can't guarantee this, e.g. user input.
var fontSizes = [72, 36, 28, 14, 12, 10, 5, 2],
    text = 'Measure me!';

// Default styles.
ctx.textBaseline = 'top';
ctx.fillStyle = 'blue';

var textDimensions,
    i = 0;

do {
   ctx.font = fontSizes[i++] + 'px Arial';
   textDimensions = ctx.measureText(text);        
} while (textDimensions.width >= canvas.width);

ctx.fillText(text, (canvas.width - textDimensions.width) / 2, 10);​

jsFiddle.

у меня есть список размеров шрифтов в порядке убывания и я перебираю список, определяя, будет ли отображаемый текст соответствовать размерам холста.

если это будет, я выровняю текстовый центр. Если вы должны иметь отступ слева и справа от текста (который будет выглядеть лучше), добавьте значение отступа в textDimensions.width при расчете, если текст будет соответствовать.

если у вас есть длинный список размеров шрифтов, чтобы попробовать, вам лучше использовать алгоритм бинарного поиска. Это увеличит сложность вашего кодекса, однако.

например, если у вас есть 200 размеров шрифта, линейная итерация через элементы массива будет O (n).

двоичный ЧОП должен быть O (log n).

здесь мужество функции.

var textWidth = (function me(fontSizes, min, max) {

    var index = Math.floor((min + max) / 2);

    ctx.font = fontSizes[index] + 'px Arial';

    var textWidth = ctx.measureText(text).width;

    if (min > max) {
        return textWidth;
    }

    if (textWidth > canvas.width) {
        return me(fontSizes, min, index - 1);
    } else {
        return me(fontSizes, index + 1, max);
    }

})(fontSizes, 0, fontSizes.length - 1);

jsFiddle.


У меня есть следующий код и он прекрасно работает для меня. Возможно, есть небольшая ошибка, но мне не нужен список размеров шрифта. Я просто получаю ширину с размером шрифта, и если он слишком большой, я вычисляю пропорциональный размер в зависимости от ширины холста и заполнения (50)

ctx.font = FONT_SIZE+"pt Verdana";
var textWidth = ctx.measureText(markText).width;
if(textWidth > canvas.width - 50) {
    ctx.font = parseInt(FONT_SIZE*(canvas.width-50)/textWidth)+"pt Verdana";
    textWidth = ctx.measureText(markText).width;
}
ctx.textBaseline = "middle";
ctx.fillStyle = "rgba(255,255,255,0.5)";
ctx.strokeStyle = "rgba(0,0,0,0.5)";
var xT = image.width / 2 - textWidth / 2;
var yT = image.height / 2;
ctx.fillText(markText, xT, yT);
ctx.strokeText(markText, xT, yT);