根据 CSS 规则测量文本的高度 - _无需浏览器渲染_ - 用于虚拟列表,提前指定高度

Measuring the height of text according to CSS rules – _without a browser rendering_ – for use with a virtualized list, to specify heights in advance

我一直在用 Electron (Chrome) 和 React 实现聊天客户端。我们的首要任务是速度。因此,我们应该使用虚拟化列表组件(也称为 "buffered render" 或 "window render")。我们探索了 react-virtualized、react-window 和 react-infinite 等。

所有这些组件的一个共同问题是,如果支持可变高度的列表元素,则需要提前知道高度。现在,有些聊天很长,有些很短,这对我们来说是一个挑战。 (图像和视频很容易感谢 EXIF 数据和 ffprobe)。

因此,我们面临着测量高度的挑战,同时还要努力提高性能。一种显而易见的技术是将元素放在视口外的浏览器容器中,执行测量,然后呈现列表。但这在性能要求方面伤害了我们。像 react-virtualized/CellMeasurer(不再由原作者维护)和 react-window 这样的软件使我们使用了这种技术,内置到库中,但是性能有点慢而且不可靠。一个可能更高性能的类似想法是使用后台 Electron 浏览器 window 进行渲染和测量,但我的直觉是那样不会更快。

我认为必须有一些解决方法可以根据自动换行、最大宽度和字体规则预先计算出字符串高度。

我目前的想法是使用像 string-pixel-width in order to calculate row heights as soon as we get the text data through our API. Basically, the library uses this piece of code to generate a map of character widths [*]. Then, once we know how wide each text, we separate each line when it maxes out the computed max row width, and finally infer list element height through row count. It's going to require a little bit of algorithmic fiddling due to break-word but there are libraries to help with that – css-line-break 这样的库似乎很有前途。

[*] 我们必须稍微修改它以考虑所有 Unicode 字符范围,但这是微不足道的。

我还没有完全探索的一些选项包括 pythonweasyprint 项目和 facebook-yoga 项目。我愿意接受你的想法!

使用 canvas 功能来测量文本可以高效地解决这个问题。

Electrons canvas text is calculated the same as the regular text, there are some diffrences in rendering though especially in reguard of anti-aliasing but that does not affect the calculation.

您可以通过

从任何文本中获取 TextMetrics
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

// Set your font parameters
// Docs: https://developer.mozilla.org/en-US/docs/Web/CSS/font
ctx.font = "30px Arial";

// returns a TextMetrics object
// Docs: https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics
const text = ctx.measureText('Hello world')

这不包括换行和自动换行,对于这个功能我建议你使用text package from pixijs,它已经使用了这个方法。此外,您可以通过在电子中启用实验性铬 TextMetrics 功能并使用它来分叉源代码(麻省理工学院许可证)并修改它以获得额外的性能。

这可以在创建 window

时完成
new BrowserWindow({
  // ... rest of your window config ...

  webPreferences: {
    experimentalFeatures: true
  }
})

现在回到我在评论中提到的部分,因为我不知道你的代码库、你的计算以及渲染过程中应该发生的一切。如果不是这种情况,您绝对应该将代码从主进程移至渲染进程,如果您执行文件访问操作或任何特定于节点的操作,您仍然应该这样做,但在所谓的预加载脚本中

这是 webPreferences 中的附加参数

webPreferences: {
  preload: path.join(__dirname, 'preload.js')
  experimentalFeatures: true
}

在此脚本中,您无需使用 IPC 调用即可完全访问节点,包括本机节点模块。我不鼓励对多次调用的任何类型的函数进行 IPC 调用的原因是它本质上很慢,您需要 serialize/deserialize 才能使它们工作。 Electron 的默认行为更糟,因为它使用 JSON,除非你使用 ArrayBuffers.