Рассчитать ширину текста с помощью JavaScript - PullRequest
418 голосов
/ 23 сентября 2008

Я бы хотел использовать JavaScript для вычисления ширины строки. Возможно ли это без использования моноширинной гарнитуры?

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

Ответы [ 22 ]

325 голосов
/ 23 сентября 2008

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

var fontSize = 12;
var test = document.getElementById("Test");
test.style.fontSize = fontSize;
var height = (test.clientHeight + 1) + "px";
var width = (test.clientWidth + 1) + "px"

console.log(height, width);
#Test
{
    position: absolute;
    visibility: hidden;
    height: auto;
    width: auto;
    white-space: nowrap; /* Thanks to Herb Caudill comment */
}
<div id="Test">
    abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
</div>
321 голосов
/ 09 января 2014

В HTML 5 вы можете просто использовать метод Canvas.measureText (дальнейшее объяснение здесь ).

Попробуйте эту скрипку :

/**
 * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
 * 
 * @param {String} text The text to be rendered.
 * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
 * 
 * @see /132635/rasschitat-shirinu-teksta-s-pomoschy-javascript
 */
function getTextWidth(text, font) {
    // re-use canvas object for better performance
    var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
    var context = canvas.getContext("2d");
    context.font = font;
    var metrics = context.measureText(text);
    return metrics.width;
}

console.log(getTextWidth("hello there!", "bold 12pt arial"));  // close to 86

Эта скрипка сравнивает этот метод Canvas с вариацией основанного на DOM метода Боба Монтеверде , так что вы можете анализировать и сравнивать точность результатов.

У этого подхода есть несколько преимуществ, в том числе:

ПРИМЕЧАНИЕ. При добавлении текста в DOM не забывайте также учитывать отступы, поля и границы .

ПРИМЕЧАНИЕ 2. В некоторых браузерах этот метод дает субпиксельную точность (результат - число с плавающей запятой), в других - нет (результат - только целое число). Возможно, вы захотите запустить Math.floor (или Math.ceil) для результата, чтобы избежать несоответствий. Поскольку метод на основе DOM никогда не является субпиксельным с точностью, этот метод имеет даже более высокую точность, чем другие методы здесь.

Согласно этому jsperf (благодаря авторам в комментариях), метод Canvas и основанный на DOM метод примерно одинаковы, если кэшировать добавляется к основанному на DOM методу , и вы не используете Firefox. В Firefox по какой-то причине этот метод Canvas намного быстрее, чем основанный на DOM метод (по состоянию на сентябрь 2014 года).

106 голосов
/ 19 февраля 2011

Вот один, который я взбил без примера. Похоже, мы все на одной странице.

String.prototype.width = function(font) {
  var f = font || '12px arial',
      o = $('<div></div>')
            .text(this)
            .css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font': f})
            .appendTo($('body')),
      w = o.width();

  o.remove();

  return w;
}

Используя это просто: "a string".width()

** Добавлено white-space: nowrap, чтобы можно было рассчитать строки с шириной, превышающей ширину окна.

31 голосов
/ 22 января 2010

JQuery:

(function($) {

 $.textMetrics = function(el) {

  var h = 0, w = 0;

  var div = document.createElement('div');
  document.body.appendChild(div);
  $(div).css({
   position: 'absolute',
   left: -1000,
   top: -1000,
   display: 'none'
  });

  $(div).html($(el).html());
  var styles = ['font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing'];
  $(styles).each(function() {
   var s = this.toString();
   $(div).css(s, $(el).css(s));
  });

  h = $(div).outerHeight();
  w = $(div).outerWidth();

  $(div).remove();

  var ret = {
   height: h,
   width: w
  };

  return ret;
 }

})(jQuery);
23 голосов
/ 27 октября 2010

Это работает для меня ...

// Handy JavaScript to measure the size taken to render the supplied text;
// you can supply additional style information too if you have it.

function measureText(pText, pFontSize, pStyle) {
    var lDiv = document.createElement('div');

    document.body.appendChild(lDiv);

    if (pStyle != null) {
        lDiv.style = pStyle;
    }
    lDiv.style.fontSize = "" + pFontSize + "px";
    lDiv.style.position = "absolute";
    lDiv.style.left = -1000;
    lDiv.style.top = -1000;

    lDiv.innerHTML = pText;

    var lResult = {
        width: lDiv.clientWidth,
        height: lDiv.clientHeight
    };

    document.body.removeChild(lDiv);
    lDiv = null;

    return lResult;
}
19 голосов
/ 23 сентября 2008

Библиотека ExtJS javascript имеет отличный класс Ext.util.TextMetrics, который "обеспечивает точные измерения пикселей для блоков текста, чтобы вы могли точно определить, насколько высоки и широки в пикселях данный блок текста будет ". Вы можете использовать его напрямую или просмотреть его исходный код, чтобы увидеть, как это делается.

http://docs.sencha.com/extjs/6.5.3/modern/Ext.util.TextMetrics.html

10 голосов
/ 23 февраля 2014

Я написал небольшой инструмент для этого. Возможно, это кому-нибудь пригодится. Работает без jQuery .

https://github.com/schickling/calculate-size

Использование:

var size = calculateSize("Hello world!", {
   font: 'Arial',
   fontSize: '12px'
});

console.log(size.width); // 65
console.log(size.height); // 14

Скрипка: http://jsfiddle.net/PEvL8/

9 голосов
/ 09 января 2018

Мне нравится твоя "единственная идея" просто сделать статическую карту ширины символов! Это на самом деле хорошо работает для моих целей. Иногда по соображениям производительности или из-за того, что у вас нет простого доступа к DOM, вам может понадобиться быстрый взломанный автономный калькулятор, откалиброванный для одного шрифта. Итак, вот один, откалиброванный для Helvetica; передать строку и (необязательно) размер шрифта:

function measureText(str, fontSize = 10) {
  const widths = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2796875,0.2765625,0.3546875,0.5546875,0.5546875,0.8890625,0.665625,0.190625,0.3328125,0.3328125,0.3890625,0.5828125,0.2765625,0.3328125,0.2765625,0.3015625,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.2765625,0.2765625,0.584375,0.5828125,0.584375,0.5546875,1.0140625,0.665625,0.665625,0.721875,0.721875,0.665625,0.609375,0.7765625,0.721875,0.2765625,0.5,0.665625,0.5546875,0.8328125,0.721875,0.7765625,0.665625,0.7765625,0.721875,0.665625,0.609375,0.721875,0.665625,0.94375,0.665625,0.665625,0.609375,0.2765625,0.3546875,0.2765625,0.4765625,0.5546875,0.3328125,0.5546875,0.5546875,0.5,0.5546875,0.5546875,0.2765625,0.5546875,0.5546875,0.221875,0.240625,0.5,0.221875,0.8328125,0.5546875,0.5546875,0.5546875,0.5546875,0.3328125,0.5,0.2765625,0.5546875,0.5,0.721875,0.5,0.5,0.5,0.3546875,0.259375,0.353125,0.5890625]
  const avg = 0.5279276315789471
  return str
    .split('')
    .map(c => c.charCodeAt(0) < widths.length ? widths[c.charCodeAt(0)] : avg)
    .reduce((cur, acc) => acc + cur) * fontSize
}

Этот гигантский уродливый массив имеет ширину символов ASCII, проиндексированную кодом символов. Так что это просто поддерживает ASCII (в противном случае предполагается средняя ширина символов). К счастью, ширина в основном линейно масштабируется в зависимости от размера шрифта, поэтому она отлично работает при любом размере шрифта. Ему явно не хватает понимания кернинга, лигатур или чего-то еще.

Чтобы «откалибровать», я просто визуализировал каждый символ до charCode 126 (могущественная тильда) в SVG, получил ограничивающий прямоугольник и сохранил его в этом массиве; больше кода и объяснения и демо здесь .

6 голосов
/ 12 января 2013

Вы можете использовать холст, чтобы вам не приходилось так много работать со свойствами CSS:

var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.font = "20pt Arial";  // This can be set programmaticly from the element's font-style if desired
var textWidth = ctx.measureText($("#myElement").text()).width;
4 голосов
/ 23 сентября 2008
<span id="text">Text</span>

<script>
var textWidth = document.getElementById("text").offsetWidth;
</script>

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...