首页 > 解决方案 > 等宽字符的精确宽度

问题描述

我正在使用带有语法突出显示的代码编辑器,我需要知道等宽字符的精确宽度。我使用这个值来计算一个字符在一行上的位置,我需要知道它以便放置各种 GUI 元素,例如文本光标(可以有多个)、选择矩形、警告工具提示等。到目前为止我一直在使用以下功能:

    function getCharacterWidth(char, fontFamily, fontSize) {
        let span = document.createElement("span");
        span.style.fontFamily = fontFamily;
        span.style.fontSize = fontSize;
        span.style.position = "absolute";
        span.style.visibility = "hidden";
        span.style.width = "auto";
        span.style.whiteSpace = "nowrap";
        span.style.padding = "0";
        span.style.margin = "0";
        span.style.letterSpacing = "0px";
        span.style.wordSpacing = "0px";
        span.innerText = char;
        document.body.appendChild(span);
        
        let width = span.getBoundingClientRect().width;
        span.remove();
        
        return width;
    }

它一直运行良好,但后来我注意到 Google Chrome 出现问题。当我的文本编辑器渲染包含数千个字符的大行时,由于舍入问题,字符位置没有被正确计算。似乎在谷歌浏览器上,width返回getBoundingClientRect()的精度最多为小数点后 5 位,这对我的用例来说并不理想。在 Firefox 上,精度似乎要高得多,达到小数点后 15 位,这就是为什么我在那里从来没有遇到过这个问题。

经过一番挖掘,我听说了根据包含数千个字符的跨度宽度来计算字符宽度的想法(https://stackoverflow.com/a/56379770/2197150)。所以,在我原来的函数中,我替换span.innerText = charspan.innerText = char.repeat(10000)并返回了width / 10000. 它有所帮助,但是当我处理大行时,计算仍然明显不正确。

所以我在这里。如何在其他浏览器中像 Firefox 一样以高精度计算字符的宽度?

标签: javascriptcross-browser

解决方案


我想出了一个对我的用例运行良好的解决方案。这个想法是根据我的编辑器的最大行来计算字符的宽度。我的线元素看起来像这样:

<div class="line"><span class="keyword">let</span><span class="space"> </span><span class="identifier">foo</span></div>

如果我们想根据该行计算字符的宽度,我们可以这样做:

let lineElement = document.querySelector(".line");
let lineWidth = lineElement.getBoundingClientRect().width;
let charWidth = lineWidth / lineElement.textContent.length;

我跟踪我在编辑器中渲染的最大行。每当我渲染一条新的最大线时,我都会charWidth根据该新线进行更新。

这使我可以通过简单的乘法计算一行内任何给定列的字符位置column * charWidth。到目前为止,它一直运行良好,即使对于超过 100000 个字符的行也是如此。

然而,我必须做的最后一件事是处理更大的行。Google Chrome 上似乎有一个错误,当您尝试使用巨大的文本节点渲染一行时,例如<span>many many characters...</span>,它不会为您提供正确的元素宽度(请参阅Chrome 上大元素的不准确宽度)。为了克服这个问题,每当我必须渲染一个巨大的文本时,我都会将它分成多个跨度,例如<span>many many </span><span>characters...</span>.

现在我可以毫无问题地渲染 500000 个字符长的行,字体大小为 48。除此之外,事情又开始变得奇怪了,计算错误和其他奇怪的浏览器行为。所以我决定设置一个在一行中呈现 500000 个字符的硬性限制。第 500000 个字符以外的所有内容都对用户隐藏。


推荐阅读