用途
Pretext 是一个纯 JavaScript/TypeScript 库,用于快速、准确、无需 DOM 的多行文本测量与排版。它不调用 getBoundingClientRect 和 offsetHeight(这些会触发昂贵的布局重排),而是实现自己的测量逻辑,并以浏览器字体引擎作为基准真值。
主要能力
- 无 DOM 测量段落高度:返回总像素高度与换行行数。
- 手动逐行布局:获取固定宽度下的所有行,每行包含文本、宽度及起止游标。
- 行统计:只返回行数与最大行宽,不产生字符串分配。
- 自然宽度测量:文本不换行时的宽度,可用于按钮尺寸计算。
- 可变宽度布局:支持文字环绕浮动图片等场景,逐行按不同宽度排版。
- 迭代器 API:按固定宽度逐行产出文本与宽度。
- 富文本内联流:支持混合字体、chip、@提及、内联代码片段等,可设置 break: 'never' 使元素保持原子性。
- 富文本内联统计:返回行数与最大行宽。
使用方式
安装:
npm install @chenglou/pretext
核心概念:
- prepare() / prepareWithSegments():一次性分析,规范化空白、切分文本、通过 canvas 测量。缓存并复用该结果。
- layout() / layoutWithLines() 等:廉价热路径,对缓存宽度做纯算术。每次 resize 调用 layout(),而不是 prepare()。
- 字体字符串格式:与 CanvasRenderingContext2D.font 相同,例如 '16px Inter'、'700 18px "Helvetica Neue"'。
选项:
{ whiteSpace?: 'normal' | 'pre-wrap' // 默认 'normal' wordBreak?: 'normal' | 'keep-all' // 默认 'normal' }
常用模式包括:虚拟列表行高预测量、二分搜索平衡文本宽度、resize 处理模式(文本/字体变化时才重新 prepare)、动态内容防布局偏移(先测量预留高度再插入 DOM)。
适用场景
- 需要在插入 DOM 前测量文本高度以预留空间、防止布局偏移。
- 虚拟化列表中预先计算每行高度。
- 需要逐行手动渲染到 canvas。
- 文字环绕浮动图片等可变宽度排版。
- 混合字体、chip、@提及、内联代码的富文本内联流。
- 需要 shrinkwrap 容器宽度(取最宽行)的场景。
限制
- 服务端支持标注为“即将推出”,当前需要浏览器环境(canvas API)。
- 字体字符串必须与 CSS font 简写完全一致(字重、样式、字号、字体族都重要)。
- lineHeight 必须与 CSS line-height 的像素值一致。
- 调用 prepare() 前字体必须已加载,可使用 document.fonts.ready 或 FontFace.load()。
- prepare() 较慢,只应在文本或字体变化时调用;resize 时只调用 layout() 或等价函数。
- CJK 文本换行不正确时可尝试 { wordBreak: 'keep-all' }。
- 富文本内联项被错误断行时,为 chip、提及、徽章添加 break: 'never'。
API 速查
用途一(仅高度):
- prepare(text, font, opts?):一次性分析,返回 PreparedText。
- layout(prepared, maxWidth, lineHeight):返回 { height, lineCount }。
用途二(手动布局):
- prepareWithSegments(text, font, opts?):一次性分析,返回 PreparedTextWithSegments。
- layoutWithLines(prepared, maxWidth, lineHeight):返回 { height, lineCount, lines[] }。
- walkLineRanges(prepared, maxWidth, onLine):逐行回调,无字符串分配。
- measureLineStats(prepared, maxWidth):仅返回 { lineCount, maxLineWidth }。
- measureNaturalWidth(prepared):文本不换行时的宽度。
- layoutNextLineRange(prepared, cursor, maxWidth):迭代器,一次一个范围,可变宽度。
- layoutNextLine(prepared, cursor, maxWidth):迭代器,一次一行加文本。
- materializeLineRange(prepared, range):范围转 LayoutLine(含文本字符串)。
用途三(富文本内联):
- prepareRichInline(items)
- walkRichInlineLineRanges(prepared, width, callback)
- materializeRichInlineLineRange(prepared, range)
- measureRichInlineStats(prepared, containerWidth)