我想使用JavaScript来计算字符串的宽度。如果不使用单行字体,这可能吗?

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


当前回答

下面的代码片段,“计算”span标签的宽度,如果它太长,就添加“…”,并减少文本长度,直到它适合它的父标签(或者直到它尝试了超过1000次)

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

其他回答

这对我很有用……

// 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.textContent = pText;

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

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

    return lResult;
}

在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
  const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
  const context = canvas.getContext("2d");
  context.font = font;
  const metrics = context.measureText(text);
  return metrics.width;
}

function getCssStyle(element, prop) {
    return window.getComputedStyle(element, null).getPropertyValue(prop);
}

function getCanvasFont(el = document.body) {
  const fontWeight = getCssStyle(el, 'font-weight') || 'normal';
  const fontSize = getCssStyle(el, 'font-size') || '16px';
  const fontFamily = getCssStyle(el, 'font-family') || 'Times New Roman';
  
  return `${fontWeight} ${fontSize} ${fontFamily}`;
}

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

如果你想使用特定元素myl的字体大小,你可以使用getCanvasFont实用函数:

const fontSize = getTextWidth(text, getCanvasFont(myEl));
// do something with fontSize here...

解释:getCanvasFontSize函数接受某些元素的字体(默认为正文),并将其转换为与上下文兼容的格式。字体属性。当然,任何元素在使用之前都必须首先添加到DOM中,否则会给您虚假的值。

更多的笔记

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

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

注意:当你在DOM中添加文本时,记住还要考虑到填充、边距和边框。

注2:在某些浏览器上,该方法产生亚像素精度(结果是浮点数),在其他浏览器上则不是(结果只是一个int)。你可能想运行数学。floor(或Math.ceil),以避免不一致。由于基于dom的方法从来没有亚像素精度,因此该方法比这里的其他方法具有更高的精度。

根据这个jsperf(感谢评论中的贡献者),如果将缓存添加到基于dom的方法中,并且您没有使用Firefox,那么Canvas方法和基于dom的方法几乎一样快。在Firefox中,由于某种原因,这种Canvas方法要比基于dom的方法快得多(截至2014年9月)。

性能

本文将此Canvas方法与Bob Monteverde基于dom的方法的变体进行了比较,因此您可以分析和比较结果的准确性。

用下面的样式创建一个DIV。在JavaScript中,设置你要测量的字体大小和属性,将字符串放入DIV中,然后读取DIV的当前宽度和高度,它将拉伸以适应内容,大小将在字符串渲染大小的几个像素内。

var fontSize = 12; var test = document.getElementById(" test "); test.style.fontSize = fontSize; Var高度= (test。clientHeight + 1) + "px"; Var宽度=(测试。clientWidth + 1) + "px" console.log(高度、宽度); #测试 { 位置:绝对的; 可见性:隐藏; 高度:汽车; 宽度:汽车; 空白:nowrap;}/*感谢Herb Caudill的评论*/ } < div id = "测试" > abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ < / div >

嘿,大家,我知道我来晚了一点,但我们开始吧

window.addEventListener("error",function(e){ alert(e.message); });
var canvas = new OffscreenCanvas(400, 50);
var ctx = canvas.getContext("2d");
ctx.font = "16px Ariel"; //this can be dynamic using getComputedStyle
const chars = ["a","b","c","d","e","f"," ","    "];
const charWidths = new Map();
while(chars.length > 0){
  var char = chars.shift();
  var wide = ctx.measureText(char).width;
  charWidths.set(char,wide);
}

然后你可以用它来做如下的事情:

var pixelWidth = charWidths.get("0");
//fyi css properties like letter-spacing need to be accounted for

我喜欢你的“唯一的想法”只是做一个静态字符宽度地图!它实际上很适合我的目的。有时,出于性能原因,或者因为不容易访问DOM,您可能只想要一个快速的、独立的、校准为单一字体的计算器。这是一个用Helvetica字体校准的;传递一个字符串和字体大小:

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

function measureText(str, fontSize) {
  return Array.from(str).reduce(
    (acc, cur) => acc + (widths[cur.charCodeAt(0)] ?? avg), 0
  ) * fontSize
}

这个巨大的丑陋数组是由字符代码索引的ASCII字符宽度。所以这只支持ASCII(否则它假设平均字符宽度)。幸运的是,宽度基本上是随字体大小线性缩放的,所以它在任何字体大小下都能很好地工作。它明显缺乏对字距或结扎之类的意识。

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