Oasis Editor:基于Canvas自研渲染引擎的开源文档编辑器

重新思考文档编辑器的技术路线
在浏览器中构建富文本编辑器,几乎所有主流方案都绑定在 contenteditable 上。
contenteditable 是 HTML5 提供的一个全局属性,当元素设置为 contenteditable="true" 时,用户可以直接在该元素内编辑文本内容。这个特性最早由 Internet Explorer 5.5(2000年发布)引入,最初是为了支持 Outlook Web Access 的邮件编辑功能,后来被纳入 W3C 标准。Mozilla Firefox 在 2003 年跟进实现,Safari 在 2005 年加入支持,直到 2014 年 HTML5 标准正式发布,contenteditable 才被正式纳入 W3C 规范。它的核心优势是开发成本低——浏览器自动处理光标、选区、输入法和文本渲染。但正因为依赖浏览器实现,不同浏览器(Chrome、Firefox、Safari)在处理复杂场景时存在显著差异:光标定位算法不同、删除行为不一致、HTML 结构清理策略各异。更关键的问题在于,W3C 规范只定义了属性的存在和基本语义,并未规定具体的编辑行为——比如按下 Enter 键时应该插入 <br>、<p> 还是 <div>,不同浏览器给出了完全不同的答案。这种"规范不充分"的现状催生了 Medium Editor 作者 Yehuda Katz 著名的吐槽:"contenteditable is terrible",也直接推动了 ProseMirror 等框架的诞生。
这种跨浏览器不一致的根源在于 contenteditable 的设计哲学缺陷:它试图用一个布尔属性来抽象整套编辑行为,但编辑行为的复杂度远超一个属性能承载的语义。浏览器厂商各自实现了 execCommand() API 来支持编辑操作(如加粗、插入列表等),但这套 API 从未被严格标准化,W3C 甚至在 2015 年将其标记为"已过时"。Google 在 2015-2016 年间曾推动 Input Events Level 2 规范,试图通过 beforeinput 事件让开发者拦截和自定义编辑行为,但直到今天各浏览器的实现仍不完全一致。这也是为什么 ProseMirror、Slate、Lexical 等现代编辑器框架虽然仍使用 contenteditable 作为输入层,但都选择接管 DOM 操作——它们监听输入事件,阻止浏览器默认行为,然后根据自己的文档模型重新渲染 DOM。
从 Google Docs 早期到 Notion、Quill、ProseMirror,contenteditable 提供了浏览器原生的可编辑能力,但也带来了这个不用我讲的痛点:跨浏览器行为不一致、光标与选区难以精确控制、复杂排版(分页、表格、图文混排)极难实现像素级掌控。这导致开发者需要编写大量兼容性代码,甚至主动禁用浏览器默认行为,自己实现编辑逻辑。对于追求像素级控制的专业文档编辑器而言,contenteditable 的"黑盒"特性成为了技术天花板。
开发者 celsowm 在 Reddit 的 r/opensource 社区公开了一个名为 Oasis Editor 的开源项目,选择了一条更激进的技术路线——自研 Canvas 渲染引擎,完全绕开 contenteditable。
Canvas 是 HTML5 提供的位图绘图 API,开发者通过 JavaScript 直接在画布上绘制像素。在文档编辑器场景中,通常使用的是 Canvas 2D Context,它提供了 fillText()、strokeRect()、drawImage() 等 API 进行位图绘制。与 WebGL(基于 OpenGL ES 的 GPU 加速 3D 渲染 API)相比,Canvas 2D 的 fillText() 可以直接利用操作系统的字体光栅化引擎(如 Windows 的 DirectWrite、macOS 的 Core Text),渲染质量高且支持亚像素抗锯齿,更适合文档编辑场景。值得注意的是,Canvas 2D 在现代浏览器中也已获得 GPU 加速——Chrome 的 Skia 后端会将 Canvas 2D 操作转译为 GPU 指令,性能表现已大幅提升。
Canvas 2D 的性能演进在近年来尤为显著。Chrome 在 2020 年左右引入基于 Skia 的 GPU 加速 Canvas 2D 后端后,大多数 2D 绘图操作实际上已在 GPU 上执行。此外,OffscreenCanvas API 允许在 Web Worker 中进行 Canvas 绘图,彻底避免主线程阻塞——这对文档编辑器至关重要,因为复杂文档的重绘可能涉及数千个绘图调用。Google Docs 在 2021 年宣布迁移到 Canvas 渲染时,正是利用了这些现代浏览器能力。另一个值得关注的技术是 Canvas 2D 的 TextMetrics API,它提供了 actualBoundingBoxAscent、actualBoundingBoxDescent 等精确的字形度量信息,使自研排版引擎能够精确计算行高和基线对齐,这是实现专业级文本排版的基础设施。
与 DOM 不同,Canvas 不维护元素树结构,所有内容都是"画"上去的图像。在文档编辑器中使用 Canvas 渲染,意味着每个文字、每条线、每个表格边框都需要开发者手动计算坐标并调用绘图 API。这意味着文本、选区、图片、表格乃至整个文档几何(document geometry)都由项目自己的引擎负责绘制和管理。
然而,Canvas 渲染方案面临的最大非技术挑战之一是无障碍访问(Accessibility, a11y)。由于 Canvas 内容对屏幕阅读器(如 NVDA、VoiceOver、JAWS)来说是一个不透明的位图,编辑器必须维护一个平行的 ARIA(Accessible Rich Internet Applications)树或隐藏的 DOM 结构来向辅助技术传达文档内容和结构。Google Docs 在迁移到 Canvas 渲染后,采用的方案是在 Canvas 层下方维护一个不可见但可被屏幕阅读器访问的 DOM 层。这不仅增加了工程复杂度,还需要确保两层结构的实时同步。此外,操作系统级别的文本选择、拼写检查、右键菜单等功能在 Canvas 模式下也需要完全自行实现,包括 IME(输入法编辑器)的候选窗口定位——这在处理中日韩文字输入时尤为关键,因为 IME 需要知道当前光标的精确屏幕坐标才能正确显示候选词窗口。
对于长期被 contenteditable 各种边界问题困扰的前端开发者来说,这是一个值得关注的尝试。
为什么要自建 Canvas 渲染引擎
contenteditable 的天然局限
contenteditable 本质上是把排版和渲染的控制权交给了浏览器。简单场景下确实省事,但一旦需要实现分页布局(paged layout)——也就是像 Word 那样将文档按 A4 纸张分页显示——浏览器原生能力就显得力不从心。
分页需要精确计算每一行、每张图片、每个表格在页面中的位置,内容溢出时还要自动跨页处理。浏览器的流式布局(flow layout,也称 normal flow)是 CSS 规范定义的默认布局模式:元素从上到下、从左到右依次排列,宽度自适应容器,高度由内容撑开。这种模型天然假设文档是一个"无限长卷轴"。而分页布局需要引入"页面"这个固定尺寸的容器概念:每一页有明确的宽高(如 A4 纸 210mm × 297mm),页面内有页边距、页眉页脚区域,正文区域的可用空间是固定的。当一个段落或表格无法在当前页面剩余空间中容纳时,引擎必须执行"分页决策"——决定从哪里断开内容、如何处理"孤行"和"寡行"(widows and orphans,排版术语,指段首或段末落单在另一页的行)。CSS 虽然有 @page 规则和 break-before/break-after 属性,但浏览器实现参差不齐,且无法满足专业排版对分页控制的精度要求。这类几何计算在 DOM 层面难以稳定实现,也是 contenteditable 方案最大的瓶颈之一。
自研分页布局引擎本质上是在浏览器中重新实现一个微型排版系统,这涉及多个经典排版算法问题。断行算法是其中的核心——Knuth-Plass 算法是 TeX 使用的最优断行算法,它通过动态规划在整个段落范围内寻找全局最优的断行方案,而非贪心地逐行填充,这样可以避免某些行过于稀疏或紧凑。**连字处理(hyphenation)**需要内置语言词典来决定单词可以在哪些位置断开。**双向文本排列(BiDi)**处理阿拉伯语、希伯来语与英文混排时的方向问题,Unicode BiDi 算法(UAX #9)包含超过 20 条规则,实现复杂度极高。表格布局本身就是 CSS 规范中最复杂的部分之一,涉及列宽分配算法、单元格合并、跨页断表等问题。商业级排版引擎如 Adobe InDesign 的排版核心经过了数十年的迭代,这也是为什么自研引擎通常需要在功能完整度和开发周期之间做出务实的取舍。
Canvas 方案的核心优势与代价
Oasis Editor 用 Canvas 接管渲染后,获得了对以下要素的完全控制权:
- 分页布局:精确模拟纸张页面,实现像素级的排版控制
- 文本渲染:字形排布、行高、断行逻辑完全由引擎决定
- 选区管理(selections):光标和高亮不再受浏览器实现差异影响
- 图片与表格:作为文档几何的一部分统一管理,避免 DOM 层面的布局塌陷
这种方案的核心优势是
核心要点
相关推荐

tiun.:为AI开发者打造的一站式认证与支付系统
登顶 Product Hunt 的 tiun. 为 AI 开发者提供认证、支付、账单、客户数据与分析的一体化系统,一条命令即可安装,帮助开发者当天上线付费产品。本文解析其定位、卖点与竞争格局。

Voiskey:能读懂语境的AI语音输入工具
Voiskey是一款登上Product Hunt排名第3的AI语音输入工具,能根据场景和读者自动调整语气,比打字快5倍,支持iOS、macOS、Android、Windows四大平台及100多种语言。

Axari:让AI分身接管你的安全运营琐事
Product Hunt新品Axari主打「AI分身」概念,帮安全团队自动处理重复性运营琐事,可在Slack、MS Teams中指派目标并自主推进任务。本文解析其产品逻辑、行业定位与需要冷静看待的问题。