Вычисление ширины текста с помощью JavaScript

StackOverflow https://stackoverflow.com/questions/118241

  •  02-07-2019
  •  | 
  •  

Вопрос

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

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

Это было полезно?

Решение

Создайте 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>

Другие советы

В 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 https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
 */
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), поскольку он не изменяет глобальное состояние, такое как ваш DOM.
  • Дальнейшая настройка возможна с помощью изменение дополнительных свойств текста canvas, такие как textAlign и textBaseline.

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

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

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

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

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 таким образом, можно вычислить строки с шириной, превышающей ширину окна.

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);

У меня это работает...

// 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;
}

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

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

Я написал для этого небольшой инструмент.Возможно, это кому-то пригодится.Это работает без 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/

Мне нравится ваша "единственная идея" просто сделать статическую карту ширины символов!Это действительно хорошо работает для моих целей.Иногда, по соображениям производительности или из-за того, что у вас нет простого доступа к 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 (в противном случае он предполагает среднюю ширину символа).К счастью, ширина в основном линейно масштабируется с размером шрифта, поэтому она довольно хорошо работает при любом размере шрифта.Ему заметно не хватает понимания кернинга, лигатур или чего-то еще.

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

Вы можете использовать canvas, чтобы вам не приходилось так много разбираться со свойствами 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;
<span id="text">Text</span>

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

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

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

CSS

div.places {
  width : 100px;
}
div.places span {
  white-space:nowrap;
  overflow:hidden;
}

HTML

<div class="places">
  <span>This is my house</span>
</div>
<div class="places">
  <span>And my house are your house</span>
</div>
<div class="places">
  <span>This placename is most certainly too wide to fit</span>
</div>

JavaScript (с помощью jQuery)

// loops elements classed "places" and checks if their child "span" is too long to fit
$(".places").each(function (index, item) {
    var obj = $(item).find("span");
    if (obj.length) {
        var placename = $(obj).text();
        if ($(obj).width() > $(item).width() && placename.trim().length > 0) {
            var limit = 0;
            do {
                limit++;
                                    placename = placename.substring(0, placename.length - 1);
                                    $(obj).text(placename + "...");
            } while ($(obj).width() > $(item).width() && limit < 1000)
        }
    }
});

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

function GetTextRectToPixels(obj)
{
var tmpRect = obj.getBoundingClientRect();
obj.style.width = "auto"; 
obj.style.height = "auto"; 
var Ret = obj.getBoundingClientRect(); 
obj.style.width = (tmpRect.right - tmpRect.left).toString() + "px";
obj.style.height = (tmpRect.bottom - tmpRect.top).toString() + "px"; 
return Ret;
}

Ширину и высоту текста можно получить с помощью clientWidth и clientHeight

var element = document.getElementById ("mytext");

var width = element.clientWidth;
var height = element.clientHeight;

убедитесь, что для свойства style position установлено значение absolute

element.style.position = "absolute";

не обязательно находиться внутри div, может находиться внутри p или span

Лучше всего определить, будет ли текст соответствовать размеру непосредственно перед отображением элемента.Таким образом, вы можете использовать эту функцию, которая не требует присутствия элемента на экране.

function textWidth(text, fontProp) {
    var tag = document.createElement("div");
    tag.style.position = "absolute";
    tag.style.left = "-999em";
    tag.style.whiteSpace = "nowrap";
    tag.style.font = fontProp;
    tag.innerHTML = text;

    document.body.appendChild(tag);

    var result = tag.clientWidth;

    document.body.removeChild(tag);

    return result;
}

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

if ( textWidth("Text", "bold 13px Verdana") > elementWidth) {
    ...
}

Построение из Ответ Дипака Надара, Я изменил параметры функций, чтобы они принимали стили текста и шрифта.Вам не нужно ссылаться на элемент.Кроме того, fontOptions имеют значения по умолчанию, поэтому вам не нужно указывать их все.

(function($) {
  $.format = function(format) {
    return (function(format, args) {
      return format.replace(/{(\d+)}/g, function(val, pos) {
        return typeof args[pos] !== 'undefined' ? args[pos] : val;
      });
    }(format, [].slice.call(arguments, 1)));
  };
  $.measureText = function(html, fontOptions) {
    fontOptions = $.extend({
      fontSize: '1em',
      fontStyle: 'normal',
      fontWeight: 'normal',
      fontFamily: 'arial'
    }, fontOptions);
    var $el = $('<div>', {
      html: html,
      css: {
        position: 'absolute',
        left: -1000,
        top: -1000,
        display: 'none'
      }
    }).appendTo('body');
    $(fontOptions).each(function(index, option) {
      $el.css(option, fontOptions[option]);
    });
    var h = $el.outerHeight(), w = $el.outerWidth();
    $el.remove();
    return { height: h, width: w };
  };
}(jQuery));

var dimensions = $.measureText("Hello World!", { fontWeight: 'bold', fontFamily: 'arial' });

// Font Dimensions: 94px x 18px
$('body').append('<p>').text($.format('Font Dimensions: {0}px x {1}px', dimensions.width, dimensions.height));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

На случай, если кто-нибудь еще добрался сюда в поисках способа измерить ширину строки и чтобы узнать, какой самый большой размер шрифта будет соответствовать определенной ширине, вот функция, основанная на решении @Domi с бинарным поиском:

/**
 * Find the largest font size (in pixels) that allows the string to fit in the given width.
 * 
 * @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 ?px verdana") -- note the use of ? in place of the font size.
 * @param {width} the width in pixels the string must fit in
 * @param {minFontPx} the smallest acceptable font size in pixels
 * @param {maxFontPx} the largest acceptable font size in pixels
**/
function GetTextSizeForWidth( text, font, width, minFontPx, maxFontPx )
{
    for ( ; ; )
    {
        var s = font.replace( "?", maxFontPx );
        var w = GetTextWidth( text, s );
        if ( w <= width )
        {
            return maxFontPx;
        }

        var g = ( minFontPx + maxFontPx ) / 2;

        if ( Math.round( g ) == Math.round( minFontPx ) || Math.round( g ) == Math.round( maxFontPx ) )
        {
            return g;
        }

        s = font.replace( "?", g );
        w = GetTextWidth( text, s );
        if ( w >= width )
        {
            maxFontPx = g;
        }
        else
        {
            minFontPx = g;
        }
    }
}

Я предполагаю, что это очень похоже на запись Depak, но основано на работе Луиса Лазариса, опубликованной в статье в впечатляющая веб-страница

(function($){

        $.fn.autofit = function() {             

            var hiddenDiv = $(document.createElement('div')),
            content = null;

            hiddenDiv.css('display','none');

            $('body').append(hiddenDiv);

            $(this).bind('fit keyup keydown blur update focus',function () {
                content = $(this).val();

                content = content.replace(/\n/g, '<br>');
                hiddenDiv.html(content);

                $(this).css('width', hiddenDiv.width());

            });

            return this;

        };
    })(jQuery);

Событие fit используется для выполнения вызова функции непосредственно после того, как функция будет ассоциирована с элементом управления.

например ,:$('input').автоматическая подгонка().триггер("подгонка");

Без jQuery:

String.prototype.width = function (fontSize) {
    var el,
        f = fontSize + " px arial" || '12px arial';
    el = document.createElement('div');
    el.style.position = 'absolute';
    el.style.float = "left";
    el.style.whiteSpace = 'nowrap';
    el.style.visibility = 'hidden';
    el.style.font = f;
    el.innerHTML = this;
    el = document.body.appendChild(el);
    w = el.offsetWidth;
    el.parentNode.removeChild(el);
    return w;
}

// Usage
"MyString".width(12);

Скрипка рабочего примера: http://jsfiddle.net/tdpLdqpo/1/

HTML:

<h1 id="test1">
    How wide is this text?
</h1>
<div id="result1"></div>
<hr/>
<p id="test2">
    How wide is this text?
</p>
<div id="result2"></div>
<hr/>
<p id="test3">
    How wide is this text?<br/><br/>
    f sdfj f sdlfj lfj lsdk jflsjd fljsd flj sflj sldfj lsdfjlsdjkf sfjoifoewj flsdjfl jofjlgjdlsfjsdofjisdojfsdmfnnfoisjfoi  ojfo dsjfo jdsofjsodnfo sjfoj ifjjfoewj fofew jfos fojo foew jofj s f j
</p>
<div id="result3"></div>

Код JavaScript:

function getTextWidth(text, font) {
    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;
};

$("#result1")
.text("answer: " +
    getTextWidth(
             $("#test1").text(),
             $("#test1").css("font")) + " px");

$("#result2")
    .text("answer: " +
        getTextWidth(
             $("#test2").text(),
             $("#test2").css("font")) + " px");

$("#result3")
    .text("answer: " +
        getTextWidth(
             $("#test3").text(),
             $("#test3").css("font")) + " px");

Тот Самый Element.getClientRects() метод возвращает коллекцию DOMRect объекты, которые указывают ограничивающие прямоугольники для каждого поля границы CSS в клиенте.Возвращаемое значение представляет собой набор DOMRect объекты, по одному для каждого поля границы CSS, связанного с элементом.Каждый DOMRect объект содержит доступ только для чтения left, top, right и bottom свойства, описывающие рамку в пикселях с левым верхним расположением относительно левого верхнего края окна просмотра.

Элемент.getClientRects() Автор: Участники Mozilla лицензируется в соответствии CC-BY-SA 2.5.

Суммирование всех возвращенных значений ширины прямоугольника дает общую ширину текста в пикселях.

document.getElementById('in').addEventListener('input', function (event) {
    var span = document.getElementById('text-render')
    span.innerText = event.target.value
    var rects = span.getClientRects()
    var widthSum = 0
    for (var i = 0; i < rects.length; i++) {
        widthSum += rects[i].right - rects[i].left
    }
    document.getElementById('width-sum').value = widthSum
})
<p><textarea id='in'></textarea></p>
<p><span id='text-render'></span></p>
<p>Sum of all widths: <output id='width-sum'>0</output>px</p>

Я создал крошечный модуль ES6 (использует jQuery).:

import $ from 'jquery';

const $span=$('<span>');
$span.css({
    position: 'absolute',
    display: 'none'
}).appendTo('body');

export default function(str, css){
    $span[0].style = ''; // resetting the styles being previously set
    $span.text(str).css(css || {});
    return $span.innerWidth();
}

Простота в использовании:

import stringWidth from './string_width';
const w = stringWidth('1-3', {fontSize: 12, padding: 5});

Классная вещь, которую вы могли бы заметить - она позволяет учитывать любые CSS-атрибуты, даже отступы!

var textWidth = (function (el) {
    el.style.position = 'absolute';
    el.style.top = '-1000px';
    document.body.appendChild(el);

    return function (text) {
        el.innerHTML = text;
        return el.clientWidth;
    };
})(document.createElement('div'));
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top