在富文本编辑器领域,性能问题一直是困扰开发者的顽疾。传统方案通常面临两难选择:使用纯DOM实现虽然开发简单但性能低下(特别是处理大文档时),而纯Canvas方案虽然渲染快却要重头实现所有文本交互逻辑。我在开发一款专业级Markdown编辑器时,发现当文档超过2万字时,主流编辑器就会出现明显卡顿。通过性能分析发现,DOM的布局计算(Layout Thrashing)和样式重绘(Repaint)是主要瓶颈。
这个混合架构的灵感来源于现代游戏引擎的UI系统——它们用Canvas绘制主体内容,但用DOM处理交互元素。我们将这个思路迁移到编辑器领域:Canvas负责文本主体渲染,DOM只处理光标、选区等动态元素。实测在10万字文档中,滚动流畅度提升300%,内存占用降低45%。
整个系统分为三个核心层级:
javascript复制class HybridRenderer {
constructor() {
this.canvasLayer = new OffscreenCanvas(2048, 2048);
this.domLayer = document.createElement('div');
this.virtualModel = new TextModel();
}
}
重要提示:必须禁用Canvas的默认抗锯齿(imageSmoothingEnabled=false),否则小字号文本会出现模糊。这是早期我们踩过的一个大坑。
传统ctx.fillText()在渲染大量文本时性能极差。我们采用以下方案:
glsl复制// SDF片段着色器示例
uniform sampler2D u_texture;
varying vec2 v_texCoord;
void main() {
float distance = texture2D(u_texture, v_texCoord).a;
float smoothing = 0.04;
float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, distance);
gl_FragColor = vec4(1.0, 1.0, 1.0, alpha);
}
实现难点在于两者的坐标同步:
typescript复制function syncCursorPosition(line: number, column: number) {
const canvasPos = modelToCanvas(line, column);
const domRect = canvas.getBoundingClientRect();
cursor.style.transform = `translate(${domRect.left + canvasPos.x}px, ${domRect.top + canvasPos.y}px)`;
}
测试环境:MacBook Pro M1/Chrome 120
测试文档:10万字Markdown(含代码块、表格)
| 指标 | 纯DOM方案 | 混合架构 | 提升幅度 |
|---|---|---|---|
| 首次加载耗时 | 1.8s | 0.6s | 67% |
| 滚动FPS | 24 | 60+ | 150% |
| 内存占用 | 280MB | 150MB | 46% |
| 输入延迟 | 120ms | 28ms | 77% |
初期在4K屏上出现文字尺寸异常,解决方案:
javascript复制function handleHiDPI() {
const ratio = window.devicePixelRatio || 1;
canvas.width = canvas.clientWidth * ratio;
canvas.height = canvas.clientHeight * ratio;
ctx.scale(ratio, ratio);
}
发现的问题:
解决方案:
对于超大型文档(如百万字级别),我们进一步采用:
rust复制// Rust实现的快速折行算法示例
#[wasm_bindgen]
pub fn line_break(text: &str, width: f32) -> Vec<usize> {
let mut breaks = Vec::new();
let mut current_width = 0.0;
for (i, c) in text.char_indices() {
let char_width = get_char_width(c);
if current_width + char_width > width {
breaks.push(i);
current_width = 0.0;
}
current_width += char_width;
}
breaks
}
这个架构已经在我们的开源编辑器项目中验证,处理百万行代码时仍保持60fps的流畅度。核心思想其实可以扩展到任何需要高性能文本处理的场景——比如电子表格、CAD软件等。关键在于找到DOM和Canvas的职责边界,让它们各司其职。