Pretext – DOMなしでテキストの高さを測定する純粋なJSレイアウトライブラリ
(github.com/chenglou)ブラウザでテキストが何行を占めるかを把握するのは、思った以上に厄介だ。通常は getBoundingClientRect や offsetHeight を使うが、これらの方法はブラウザにレイアウトの再計算を強制する。いわゆるレイアウトリフローで、ブラウザにとってはかなり重い処理だ。
Pretext はこの問題を別の方法で解決する。Canvas の measureText() でフォントエンジンから文字幅を直接取得し、その後の行計算はキャッシュされた幅の値を使って純粋な算術演算だけで行う。DOM にはまったくアクセスしない。
const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀', '16px Inter')
const { height, lineCount } = layout(prepared, textWidth, 20)
性能も印象的だ。500個のテキスト配置を基準にすると、prepare() は約 19ms、その後の layout() は 0.09ms 程度だ。
2つの使い方
高さだけが必要なら、prepare() + layout() の組み合わせで完結する。仮想化リストの実装、スクロール位置の維持、AI が生成したテキストがボタンの外にはみ出すかの確認といった用途に使える。
行単位でレイアウトを直接制御したいなら、layoutWithLines()、walkLineRanges()、layoutNextLine() のような API を活用する。Canvas、SVG、WebGL、サーバーサイドレンダリングにも組み込めるほか、画像の横にテキストが回り込むような、行ごとに幅が異なるレイアウトにも対応する。
絵文字、CJK、アラビア語のような双方向テキストまでサポートする。React と Relay を作った chenglou のプロジェクト。⭐ 7.1k
https://github.com/chenglou/pretext
3件のコメント
一番下のGitHubリンクの後ろに
%E2%80%8B(ゼロ幅スペース)がたくさん付いていますね。js
.replace(/\u200b/g, '')デモにある https://chenglou.me/pretext/editorial-engine/ のページが、いちばん分かりやすく示しているようですね。
詳しく理解して使っているわけではありません。あらかじめご了承ください。
canvasのmeasureTextをベースに再計算するらしいですが……その API はちょっと信用できないですね。
正確には API 自体を信用できないというより、
同じように DOM を描画する際に条件値を完全に一致させてこそ、ブラウザで見える
高さや形が API で取得したときも同一になるわけで、
そのことに気づけず、なぜ値が違うのか、どこでバグが出たのかと悩んだ
ぞっとするような悪夢があります……