Измерение высоты текста в соответствии с правилами CSS - без отображения в браузере - для использования с виртуализированным списком, для предварительного указания высоты - PullRequest
7 голосов
/ 22 декабря 2019

Я внедряю чат-клиент в Electron (Chrome) и React. Наш главный приоритет - скорость. Таким образом, нам следует использовать компонент виртуализированного списка (также известный как «буферизованный рендер» или «рендеринг окна»). Мы исследовали реакцию-виртуализацию, реакцию-окно и реакцию-бесконечность, среди прочих.

Одной из общих черт всех этих компонентов является то, что если поддерживаются элементы списка с переменной высотой, то высоты должны быть известны заранее. Сейчас некоторые чаты очень длинные, а другие очень короткие, поэтому это представляет для нас проблему. (Изображения и видео просты благодаря данным EXIF ​​и ffprobe).

Итак, мы столкнулись с проблемой измерения высоты, одновременно стремясь быть чрезвычайно производительными. Один очевидный способ - поместить элементы в контейнер браузера вне области просмотра, выполнить измерения и затем отобразить список. Но это вредит нам в отношении требований к производительности. Такие программы, как реагирующая виртуализация / CellMeasurer (которая больше не поддерживается первоначальным автором) и реагирующее окно, делают нас из этого метода встроенным в библиотеку, но производительность несколько медленная и ненадежная. Аналогичная идея, которая могла бы быть более производительной, заключалась бы в использовании фонового окна Electron Browser для визуализации и измерения, но моя интуиция заключается в том, что это не будет намного быстрее.

Я утверждаю, что должны быть некоторыерешен способ заранее определить высоту строки в соответствии с переносом слов, максимальной шириной и правилами шрифта.

Моя текущая идея - использовать такую ​​библиотеку, как string-pixel-width , чтобы вычислить высоту строк, как только мы получим текстовые данные через наш API. По сути, библиотека использует этот фрагмент кода для создания карты ширины символов [*]. Затем, как только мы узнаем ширину каждого текста, мы разделяем каждую строку, когда она максимально вычисляет максимальную ширину строки, и, наконец, выводим высоту элемента списка по количеству строк. Это потребует немного алгоритмического возмущения из-за разрывных слов, но есть библиотеки, которые помогут с этим - css-line-break кажется многообещающим.

[*] Нам пришлось бы немного его изменить, чтобы учесть все диапазоны символов Юникода, но это тривиально.

Некоторые опции, которые я еще не полностью изучил, включают в себя проект python weasyprint и проект facebook-yoga. Я открыт для ваших идей!

1 Ответ

4 голосов
/ 28 декабря 2019

Использование возможностей холста для измерения текста может решить эту проблему более эффективным способом.

Электронный текст холста рассчитывается так же, как и обычный текст, при рендеринге есть некоторые различия, хотя особенно в отношениисглаживания, но это не влияет на вычисления .

Вы можете получить 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')

Это не включает в себя строкуразрывы и переносы слов, для этой функции я бы порекомендовал вам использовать текстовый пакет из pixijs , он уже использует этот метод. Кроме того, вы можете раскошелиться на источник (лицензия MIT) и изменить его для повышения производительности, включив экспериментальные функции хрома TextMetrics в электронном режиме и используя его.

Это можно сделать при создании окна

new BrowserWindow({
  // ... rest of your window config ...

  webPreferences: {
    experimentalFeatures: true
  }
})

Теперь о той части, которую я упомянул в комментариях, поскольку я не знаю вашей кодовой базы, ваших вычислений и всего, что должно происходить в процессе рендеринга. Если это не тот случай, вам определенно следует переместить свой код из основного процесса в процесс рендеринга, если вы выполняете операции доступа к файлу или что-то специфичное для узла, вы все равно должны делать это, но в так называемом сценарии предварительной загрузки

это дополнительный параметр в webPreferences

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

В этом скрипте у вас есть полный доступ к узлу, включая собственные модули узла, без использования вызовов IPC. Причина, по которой я не одобряю вызовы IPC для любого типа функций, вызываемых несколько раз, заключается в том, что по своей природе она медленная, вам нужно сериализовать / десериализовать, чтобы они работали. Поведение по умолчанию для электрона еще хуже, поскольку он использует JSON, за исключением того, что вы используете ArrayBuffers.

...