使用方法
Pretext 快速上手指南
安装
bash
npm install @chenglou/pretext
# 或
pnpm add @chenglou/pretext1
2
3
2
3
也可以直接使用 CDN:
ts
import { prepare, layout } from 'https://cdn.jsdelivr.net/npm/@chenglou/pretext@0.0.3/dist/layout.js'1
核心 API
Pretext 只有三个核心 API,掌握它们就能应对绝大多数场景。
prepare() — 文本预处理
prepare() 做一次性工作:归一化空格、分段、应用粘合规则、用 Canvas 测量字形宽度。
ts
import { prepare } from '@chenglou/pretext'
// 参数:文本 + 字体描述
const prepared = prepare('AGI 春天到了.开始了 🚀', '16px Inter')1
2
3
4
2
3
4
字体描述格式:'字号/行高 字体名',例如 '14px/1.8 Georgia, serif'
layout() — 纯算术计算高度
layout() 是热路径:纯算术运算,不触碰 DOM!
ts
import { prepare, layout } from '@chenglou/pretext'
const prepared = prepare('文本内容', '16px/1.8 Inter')
const { height, lineCount } = layout(prepared, maxWidth, lineHeight)
// height: 段落总高度(像素)
// lineCount: 总行数1
2
3
4
5
6
7
2
3
4
5
6
7
layoutWithLines() — 获取每行文本
如果需要渲染到 Canvas 或自定义布局,获取每行文本:
ts
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext'
const prepared = prepareWithSegments('文本内容', '18px/1.8 Inter')
const { lines } = layoutWithLines(prepared, 320, 26) // 320px 最大宽度,26px 行高
for (let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i].text, 0, i * 26)
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
常见场景
场景一:测量段落高度(最常用)
ts
import { prepare, layout } from '@chenglou/pretext'
function measureHeight(text: string, font: string, maxWidth: number, lineHeight: number) {
const prepared = prepare(text, font)
const { height, lineCount } = layout(prepared, maxWidth, lineHeight)
return { height, lineCount }
}
// 使用
const { height } = measureHeight('这是要测量的文本', '15px/1.7 Georgia', 400, 25.5)
console.log(`需要 ${height}px 的高度`)1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
场景二:Textarea 自动高度
ts
import { prepare, layout } from '@chenglou/pretext'
const textarea = document.getElementById('input') as HTMLTextAreaElement
const font = '15px/1.6 Helvetica Neue, sans-serif'
const lineHeight = 24
textarea.addEventListener('input', () => {
const prepared = prepare(textarea.value, font)
const { height } = layout(prepared, textarea.clientWidth, lineHeight)
textarea.style.height = `${Math.min(height, 300)}px` // 限制最大高度
})1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
场景三:文本环绕浮动元素
ts
import { prepareWithSegments, layoutNextLine } from '@chenglou/pretext'
function flowTextAround(prepared, obstacles, columnWidth, lineHeight, startY) {
let cursor = { segmentIndex: 0, graphemeIndex: 0 }
let y = startY
const lines = []
while (true) {
// 计算当前行可用宽度(考虑障碍物)
const obsInRow = obstacles.filter(o => o.y + o.h > y && o.y < y + lineHeight)
const availableWidth = obsInRow.length > 0
? obsInRow[0].x // 障碍物左边缘之前的宽度
: columnWidth
const line = layoutNextLine(prepared, cursor, availableWidth)
if (!line || line.text.trim() === '') break
lines.push({ text: line.text, y, x: 0 })
cursor = line.end.cursor
y += lineHeight
// 跳过障碍物
const hitObs = obstacles.find(o => y >= o.y - 2 && y <= o.y + o.h + 2)
if (hitObs) y = hitObs.y + hitObs.h + 6
}
return lines
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
性能数据
在当前版本的基准测试中:
prepare()对 500 条文本的批量处理约 19ms(一次性)layout()对同样批量处理约 0.09ms(可反复调用)
支持的文本特性
- ✅ 所有主流语言(中文、阿拉伯文、希伯来文等)
- ✅ Emoji 混合(👨👩👧👦 等 ZWJ 序列)
- ✅ 混合方向文本(RTL + LTR)
- ✅ 浏览器特定 quirks 自动处理
总结
| API | 作用 | 触发 DOM |
|---|---|---|
prepare() | 一次性文本分析 + Canvas 测量 | 否 |
layout() | 根据最大宽度和行高计算文本高度 | 否 |
layoutWithLines() | 获取所有行的文本 | 否 |
layoutNextLine() | 流式布局,适合文本环绕 | 否 |
walkLineRanges() | 遍历行范围,获取宽度和光标位置 | 否 |