关于 pretext
pretext 是一个 JavaScript/TypeScript 库,使用缓存的字体度量来测量和排版多行文本,无需触发 DOM 重排。它适用于需要行布局与高度计算的渲染及服务端场景。
主要功能
- 01测量多行文本高度与行数
- 02按行布局并访问文本片段
- 03计算收缩包裹宽度
- 04设置空白、断词和字距选项
- 05输出到 DOM、Canvas、SVG 和 SSR
适用场景
- 01无需 DOM 测量即可计算段落高度
- 02排版环绕浮动图片的文本
- 03渲染多语言及表情符号文本
- 04在性能关键循环中避免重排
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ akillness/ jeo-skills --skill pretext安装后在 Claude Code 中输入/pretext确认已加载。
附带文件SKILL.toon
详细介绍
pretext — 快速多行文本测量与排版
不触发 DOM 回流。基于缓存字体度量进行纯算术计算。
pretext 是一个纯 JavaScript/TypeScript 库,可快速、准确、全面地测量和排版多行文本,且不会触发 getBoundingClientRect、offsetHeight 或任何其他 DOM 回流操作。
安装
插件(Claude Code)
claude plugin marketplace add chenglou/pretext
npm
npm install @chenglou/pretext
技能(任何平台)
npx skills add https://github.com/akillness/jeo-skills --skill pretext
适用场景
- 无需在 DOM 中测量,即可计算段落高度和行数
- 为环绕浮动图片或宽度可变的容器的文本构建手动换行布局
- 处理包含 emoji、CJK 字符、RTL 文本和多语言混合内容的富文本
- 使用准确的度量数据将测量后的文本渲染到 DOM、Canvas 或 SVG
- 实现软连字符断词、字母间距或基于区域设置的断词
- 避免在性能关键型渲染循环中触发布局回流
- 服务端文本测量(适用于 SSR)
不适用场景
- 需要完整的 CSS 文本引擎保真度 → 直接依赖 DOM 测量
- 需要 OpenType 特性支持(连字、字距调整表)→ 使用专用字体整形库
- 需要 PDF 文本排版 → 使用 PDF 生成库
- 目标环境不支持
Intl.Segmenter和 Canvas 2D
核心 API
用例 1:高度测量
通过纯算术计算段落高度和行数——调用 prepare() 后无需访问 DOM。
import { prepare, layout } from '@chenglou/pretext'
// One-time analysis + font measurement (cached)
const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀', '16px Inter')
// Pure arithmetic — no DOM interaction
const { height, lineCount } = layout(prepared, 320, 20)
// width: 320px container, lineHeight: 20px
用例 2:手动换行布局
访问每一行,以便进行自定义渲染(例如,让文本环绕浮动图片)。
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext'
const prepared = prepareWithSegments('Text here', '18px "Helvetica Neue"')
const { lines } = layoutWithLines(prepared, 320, 26)
for (const line of lines) {
// line.text, line.y, line.width, line.segments
}
用例 3:收缩包裹宽度
查找可容纳文本且不会换行的最小容器宽度。
import { prepare, shrinkWrap } from '@chenglou/pretext'
const prepared = prepare('Hello world', '14px system-ui')
const { width } = shrinkWrap(prepared, 26)
选项参考
| 选项 | 类型 | 值 | 描述 |
|---|---|---|---|
whiteSpace | string | 'normal' | 'pre-wrap' | 'pre-wrap' 会保留空白(textarea 样式) |
wordBreak | string | 'normal' | 'keep-all' | 'keep-all' 可防止 CJK 文本在词中间断行 |
letterSpacing | number | 像素 | 等效于 CSS 的字母间距 |
const prepared = prepare(text, font, {
whiteSpace: 'pre-wrap',
wordBreak: 'keep-all',
letterSpacing: 1.5,
})
要求
Intl.SegmenterAPI(基于区域设置的词语/字素分段)- Canvas 2D 文本测量(
CanvasRenderingContext2D.measureText) - 支持现代浏览器和 Node.js 16+(Node 中需安装 canvas 包)
输出目标
| 目标 | 说明 |
|---|---|
| DOM | 使用 lines + 绝对定位或 transform: translateY |
| Canvas | 使用 lines[i].y + ctx.fillText |
| SVG | 使用 lines[i].y + <text y=...> 元素 |
| SSR | 在服务端计算高度;在客户端完成位置水合 |
操作规则
- 每组唯一的(文本、字体)组合只调用一次
prepare()/prepareWithSegments()——这是耗时步骤 - 在相同文本需要按不同宽度测量时,缓存
prepared对象 - 可随时调用
layout()/layoutWithLines()——它们仅执行纯算术运算,没有副作用 - 在非浏览器环境中初始化前,务必检查
Intl.Segmenter是否可用 - 先使用
shrinkWrap查找最小宽度,再按固定容器宽度测量 - 需要逐行访问片段以进行富文本渲染时,优先使用
prepareWithSegments,而非prepare
示例
import { prepare, layout, prepareWithSegments, layoutWithLines } from '@chenglou/pretext'
// Basic height check
const p = prepare('Hello, 世界! 🌏', '16px sans-serif')
console.log(layout(p, 200, 20)) // { height: 20, lineCount: 1 }
// Variable-width layout (text around floated image)
const ps = prepareWithSegments('Long article text...', '16px Georgia')
const { lines } = layoutWithLines(ps, (lineIndex) => {
return lineIndex < 5 ? 200 : 320 // narrower for first 5 lines
}, 24)
来源:chenglou/pretext — MIT 许可证
评论
支持 Markdown