平均字符宽度是如何计算的?

3
根据HTML规范,cols属性指定每行预期的最大字符数。当使用的字体每个字符具有相同的宽度时,这是正确的。
根据w3schools,cols属性指定文本区域的宽度(以平均字符宽度为单位)。我猜他们的意思是字体的平均字符宽度而不是文本区域中文本的平均字符宽度。
现在我的问题是用户代理如何计算特定字体的平均字符宽度,包括哪些字符在此计算中。是否有一种方法可以获取此信息或自己计算?
我想要做的是,在不使用任何其他内容(只使用textContent和cols属性)的情况下计算文本区域内的可视行数。
我已经尝试过:
- 计算textContent的平均字符宽度:

const textSpan = document.getElementById("textSpan");
const textarea = document.getElementById("textarea");

textarea.addEventListener("input", (evt) => {
  textSpan.innerText = evt.target.value;
  console.log("Predicted number of lines: " + (textSpan.getBoundingClientRect().width / (textSpan.getBoundingClientRect().width / textSpan.innerText.length * 10))) // 10 = cols attribute of textarea element;
});
<textarea id="textarea" cols="10" rows="10" style="word-break: break-all;"></textarea>
<span id="textSpan"></span>

  • 计算大多数字符的平均字符宽度,这可能有效,但我必须知道用于计算的字符是什么...:

const textSpan = document.getElementById("textSpan");
const textSpanACW = document.getElementById("textSpanACW");
const textarea = document.getElementById("textarea");

textarea.addEventListener("input", (evt) => {
  textSpan.innerText = evt.target.value;
  console.log("Predicted number of lines: " + (textSpan.getBoundingClientRect().width / (textSpanACW.getBoundingClientRect().width / textSpanACW.innerText.length * 10))) // 10 = cols attribute of textarea element;
});
<textarea id="textarea" cols="10" rows="10" style="word-break: break-all;"></textarea>
<span id="textSpanACW">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ,;?.:/=+ù%µ£^¨$*-_)àç!è§('"é&|@#¼½^{[{}</span>
<span id="textSpan"></span>

我弄清楚了这一点之后,我必须在不使用 word-break: break-all; 的情况下获取可视行数。所以答案中包含这个信息会很好,但不是必需的。


更容易的比较方法是将文本区域的宽度与 span 元素的宽度进行比较,然后计算它们之间的差异。请为两者设置相同的字体族。 - G-Cyrillus
@G-Cyrillus 很好的建议,我会试试看。但我仍然对这个问题的答案感兴趣。谢谢! - Laurent Dhont
可能的例子:https://codepen.io/gc-nomade/pen/MWbpJyB?editors=1111。边框/填充/字体规则以及滚动条的显示需要考虑。 - G-Cyrillus
对于字符宽度,你可以使用等宽字体,其中每个字符都相同,其他字符的大小取决于它们的大小。浏览器在屏幕上打印它们,但不进行任何计算,你需要在打印后检索它们的总长度 :( 你只能计算它们,并注意空格。 - G-Cyrillus
@G-Cyrillus 这段代码片段的精度不够准确。是的,我知道那些字体,但不想使用它们。我也知道我必须计算它,但问题是如何计算以及使用哪些字符。 - Laurent Dhont
1个回答

0
HTML 规范 的进一步阅读中,我们可以看到:文本框元素的实际宽度是 size×avg + sbw,其中 size 是元素的字符宽度,avg 是元素主要字体的平均字符宽度(以 CSS 像素为单位),sbw 是滚动条的宽度(以 CSS 像素为单位)。 (元素的 letter-spacing 属性不影响结果)。

因此,计算平均字符宽度(avg)的公式为:(effectiveWidth-sbw) / size

获取 sbw

<style>
    /* way the hell off screen */
    .scrollbar-measure {
        width: 100px;
        height: 100px;
        overflow: scroll;
        position: absolute;
        top: -9999px;
    }
</style>
<script>
    const sbw = getScrollbarWidth();

    function getScrollbarWidth() {
        let scrollDiv = document.createElement("div");
        scrollDiv.className = "scrollbar-measure";
        document.body.appendChild(scrollDiv);
        const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
        document.body.removeChild(scrollDiv);
        return scrollbarWidth;
    }

</script>

获取大小

HTML规范中:cols属性指定每行预期的最大字符数。如果指定了cols属性,则其值必须是有效的大于零的非负整数。如果将解析非负整数的规则应用于属性的值,结果得到一个大于零的数字,则元素的字符宽度为该值;否则,它为20。

所以:

let size = parseInt(textarea.getAttribute("cols"));

if (isNaN(size)) {
    size = 20;
}

获取有效宽度

如果元素具有cols属性,并且使用解析非负整数的规则解析该属性的值不会生成错误,则用户代理应将该属性用作元素上width属性的表示提示,其值为文本区域的有效宽度(如下所定义)。否则,用户代理应该表现得好像它有一个用户代理级别的样式表规则,将元素上的width属性设置为文本区域的有效宽度。

因此:

const effectiveWidth = parseFloat(getComputedStyle(textarea).getPropertyValue("width").slice(0, -2));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接