使用JavaScript计算文字宽度

我想使用JavaScript计算字符串的宽度。是否可以不必使用等宽字体?

如果不是内置的,我唯一的想法就是为每个字符创建一个宽度表,但这是非常不合理的,特别是支持Unicode和不同类型的大小(以及与此相关的所有浏览器)。

乐JinJin2020/03/13 18:10:28

小提琴的工作示例: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");
路易达蒙2020/03/13 18:10:28

您还可以使用createRange做到这一点,它比文本克隆技术更准确:

function getNodeTextWidth(nodeWithText) {
    var textNode = $(nodeWithText).contents().filter(function () {
        return this.nodeType == Node.TEXT_NODE;
    })[0];
    var range = document.createRange();
    range.selectNode(textNode);
    return range.getBoundingClientRect().width;
}
LEY老丝2020/03/13 18:10:28

如果其他任何人都在这里寻找一种测量字符串宽度的方法以及一种知道适合特定宽度的最大字体大小的方法,则此函数基于@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;
        }
    }
}
小小神奇2020/03/13 18:10:28

试试这个代码:

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;
}
古一GO2020/03/13 18:10:28

文本的宽度和高度可以通过clientWidth获得clientHeight

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

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

确保样式位置属性设置为绝对

element.style.position = "absolute";

不需要位于a之内div,可以位于a p或a之内span

达蒙老丝2020/03/13 18:10:28

我喜欢只做一个静态字符宽度图的“唯一想法”!对于我的目的,它实际上工作得很好。有时,出于性能原因,或者由于您无法轻松访问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(否则假定平均字符宽度)。幸运的是,宽度基本上与字体大小成线性比例,因此在任何字体大小下都可以很好地工作。显然,它对字距调整或连字等均缺乏任何了解。

为了“校准”,我只是在svg上将每个字符渲染到charCode 126(强大的波浪号),并得到了边界框并将其保存到此数组中;更多代码,解释和演示在这里

神乐Tony2020/03/13 18:10:28

下面的代码片段“计算” span-tag的宽度,如果它太长,则在其后附加“ ...”,并减小文本长度,直到适合其父级为止(或直到​​尝试了超过一千次)

的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)
        }
    }
});
古一村村2020/03/13 18:10:28
<span id="text">Text</span>

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

只要<span>标记没有应用其他样式,它就应该起作用。offsetWidth将包括任何边框的宽度,水平填充,垂直滚动条的宽度等。

凯斯丁2020/03/13 18:10:27

更好的方法是在显示元素之前检测文本是否适合。因此,您可以使用此功能,而无需在屏幕上显示该元素。

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) {
    ...
}
小哥猴子2020/03/13 18:10:27

ExtJS的JavaScript库有一个名为Ext.util.TextMetrics说,“为文本块精确测量像素,让您可以准确确定的高和宽,以像素为单位,文本的给定块将是”一个伟大的阶级。您可以直接使用它,也可以查看其源代码以查看其完成方式。

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

猴子神乐2020/03/13 18:10:27

这对我有用...

// 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;
}
十三Mandy2020/03/13 18:10:27

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方法与Bob Monteverde基于DOM的方法的变体进行了比较,因此您可以分析和比较结果的准确性。

这种方法有几个优点,包括:

  • 比其他(基于DOM)方法更简洁,更安全,因为它不会更改全局状态(例如DOM)。
  • 通过修改更多的画布文本属性(例如textAlign和)可以进行进一步的自定义textBaseline

注意:将文本添加到DOM时,请记住还要考虑padding,margin和border

NOTE 2: On some browsers, this method yields sub-pixel accuracy (result is a floating point number), on others it does not (result is only an int). You might want to run Math.floor (or Math.ceil) on the result, to avoid inconsistencies. Since the DOM-based method is never sub-pixel accurate, this method has even higher precision than the other methods here.

According to this jsperf (thanks to the contributors in comments), the Canvas method and the DOM-based method are about equally fast, if caching is added to the DOM-based method and you are not using Firefox. In Firefox, for some reason, this Canvas method is much much faster than the DOM-based method (as of September 2014).

老丝村村Stafan2020/03/13 18:10:27

创建具有以下样式的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>