W3C

Web 中文兴趣组会议

2022年9月6日

题目:模拟选区在划词评论及搜索场景的实践

讲者:云澹(蚂蚁集团)[演示文稿]

现场纪要

云澹:

我的花名叫云澹。我没有做 PPT,因为我本身是做编辑器,所以习惯用文档来演示。现在我在蚂蚁集团的语雀团队负责文档编辑器的开发工作。

语雀现在已经诞生了七款编辑器,今天主要讲其中的文本编辑器。在富文本编辑器里,说起“选区”,大家都不会陌生。只要浏览器默认蓝色就表示文本已经被选中了,但现在 Web 富文本编辑器里,不仅有文本,还有图片,还有表格、嵌入图片等一系列元素,这些同样可以被选择和编辑的。

这就要提到模拟选区,选区中可能不仅是表格,可能还有容器、文本等等。

为什么要实现模拟选区?第一,因为现在的文本编辑器需要块级选区的能力,如单元格、卡片;第二,需要非连续多选区能力,如同时展示多人用户在线编辑的状态以及文本搜索;第三,在非 DOM 选区下,必须要实现自己的光标和选区;第四个是不同场景的选区,因为不同的文本样态需要不同的选区。

一个模拟选区的实现至少需要两个能力,记录当前的选区,还有标注选区。

语雀是一个分层架构,整体分为 Kernel,还有 Engine和 Editor。

为了便于理解,Kernel 层中的选区数据结构,和原生的浏览器非常接近,比如这里是原生的 DOM 选区,在view Ranges 也是一样,在虚拟选区是通过一个数据结构变成这是块型的选区。

第二点是选区高亮,DOM 已经提供了光标和选区实现,因此基于 DOM 的编辑器实现有很多方案,大致可以分为两类,第一个是基于原有的 DOM 结构通过拆标签实现;另一种是不修改原有的 DOM,通过遮罩的方式实现选区。

拆补标签方案需要把高亮的结构拆出来,比如图中 3456 需要高亮,就通过 CSS 样式标高亮就可以了,当有一个跨节点的高亮,就需要有非常复杂的标签拆分逻辑。它的优点就是选区的绘制性很好,高亮选区 CSS 样式控制,不需要自行选择,而且编辑状况下修改文本内容无需整体重绘,但缺点是涉及标签的拆分和补充,逻辑比较复杂,新插入节点,标签跳动都需要自己计算。

另外就是剪贴版需要把高亮的标签剔除掉,看起来很理想,实现起来不容易。

Mask 定位方案的思路是不侵入文档内容本身,它的优点是实现划线的交叉和融合逻辑比较简单,因为像划区,可能会独立实现。还有独立的文档,复制功能没有影响。缺点就是性能问题,因为需要渲染区的结果实现渲染,所以它频繁调用 getBoundingClienRect。需要对任何可能影响位置的情况做重绘,比如选大了都要整体重绘避免位置偏移。

第三个是 Selection API,它的缺陷是对选区做单一绘制,不能实现多个选区。

最后一个是 Highlight API。

关于 Hightlight API 的实践放到最后来讲,我们先讲一下在语雀中如何实现选区高亮。

在语雀里,单一选取比较容易实现,再有就是块级选区和文本选区。现在所有的文本选区都是通过高亮实现,通过 Canvas 遮罩实现。

块级 Canvas 实现原理非常简单,只要知道哪几个选区被选中,这就是内核层实现的模拟选区,在 DOM 层选区识别到分别在不同单元格,会把 DOM 转化为多个Range,我们允许存多个 Range,然后再加上一个简单的 CSS 样式,就可以控制到高亮效果。

这是最简单的使用场景。

第二个很常用的场景就是文本搜索,在浏览器当中,只要提供一个搜索输入框,输入一段文本,然后按一下 Ctrl + F,就可以在文本上做搜索的功能,实际上Web的文本当中除了实现选区功能,还要实现编辑功能,整体效果就是这样,提供一个面板,提供一个输入框给它,可以检索文本文档之中所有可以选取的文本,实现替换。

选取是数据行为,编辑是文本行为,我们会通过自己的算法将 dom Range 转化为 view Range,会对整篇文档构建大的 Range,在 Range 当中提取相应的文本内容,在文本的 Selection 当中选取对应的文本,同行会有多段匹配的文本,每个 Selection 会匹配到DOM Range,最后创建一个空白的画布,在对应的 DOM Range 节点的位置,选取矩形或者是高亮的背景盖在上面。

渲染的背景比较清晰,但实现起来比较困难。举一些例子,虽然我们拿到了一个 text node,我们要解析它的文本行是否折行,判断方法非常简单,拿到一个真实的文本节点,用二分法查找是否发现了折行,就是可以看到真实的 Range,看一下光标是否和高亮的位置有交叉,如果是同一行,证明它是交叉,通过这样可以得到小的 text Range,最后可以得到有三行文本。

第二个是选区重叠,其实我们经常会遇到这样的情况,比如搜索的场景,第一段文本匹配了,第二段文本又匹配了,有一个1是交叉,其实这样的交叉场景处理起来非常简单。因为绘制的过程有先后顺序,后绘制的只要把交叉部分擦除再盖上去就可以了,但是如果是 DOM 拆分的方案,就会耗费很大的精力,因为要把标签拆开再修补回来。

第三个是画布切分,因为 HTML 上有画布限制,Safari 也是有画布限制,在语雀当中对这个无感,它不会因为画布过大导致功能失常。

文本功能说完之后,说一下使用过程中最复杂的场景划词评论。因为它不仅需要处理文本交叉,还要做高亮交互。

首先是选区样式的处理,语雀上的选区和通过样式展现更好的交互效果。对划词评论来说,第一步要进行选区采集,要在选区里所有的节点进行拆分,根据节点类型做区分,可以按照文内节点、行类节点做拆分,找到对应的渲染位置,同时行内会出现一个文本节点出现多个矩形的情况,最后出现每个节点转化为矩形坐标。

[多媒体演示]像这样的情况,原本是两组节点会转化为这样的矩形坐标。最后会根据节点类型决定到底是绘制矩形还是绘制下划线。

样式问题解决了,要解决点击的问题,我们对划词评论要实现点击的效果,点击之后要实现当前的选区,画一个评论样板,如果在一个 Canvas 画布上方,怎么确定当前的点击是否需要激活呢?很简单,我们会给 Canvas 绑定事件,所有点击匹配当前的坐标,匹配当前的 X、Y 坐标是否在选区的范围内,如果是存在多个评论,我们会优先选择左上方的坐标。

下面是选区折叠,在语雀具备两个折叠能力,对划词评论来说,直接隐藏用户可能不知道这个地方有条评论,就直接跳过了。因此,我们对于折叠的划词评论有规避的逻辑,效果类似突变,原本正文的地方有个评论,当二级标题被折叠的时候,评论会归属于二级标题,当二级标题也折叠,我们就会上移,信息是累计的。

按需渲染,当文本存在上千条评论,在渲染过程中会大量获取 DOM 的渲染位置,有很大的性能损耗,我们必须分摊这个计算的压力,所以一个常规的减少渲染压力的方法,就是把一次的渲染压力分摊到多次,我们只会对视窗的内容进行渲染。实际上我们并不知道哪些内容需要渲染,我们在划词评论除了存起点和终点,还会额外存起点和终点的段落,这样在渲染之前就知道哪些评论归属于同一个段落,如果同一个段落可以一起渲染,当他们出现在同一个视窗的时候就可以进行一起渲染。

为了优化性能,我们在分组过程中会技术额外的预处理,比如相同的评论合并成一条,如果是修改过程中,段落被删除了,我们会直接进行坐标移重新计算一遍位置信息。

由此可以看到,同样的模拟选区效果,还要实现不同的场景下进行许多处理,是否可以合并呢?

也就是刚才介绍的 Highlight API,它支持了部分的样式设计,像 color、 cursor 等等,看刚才的场景用Highlight API 来做是怎样的。

因为渲染和位置是分离的,刚才还是进行文本匹配的操作,整个渲染层不用画布绘制,直接调用Highlight,直接 new一个 Highlight 对象,就可以帮我们标记出选区,看起来好像特别好,我们有不用自行处理文本换行,也不要调用 getClientBoudunding Rect。

其实,Highlight 和官方提供的其他 API 一样,它只对文本生效。因此,一段跨文本、跨段的选区,不是我们要的块,我们希望块的高亮,因此块和文本还是要拆分高。我们采集到节点的时候,就要把文本节点和块的节点做渲染处理,块文本用 Highlight 进行渲染,文本渲染还是文本渲染,我们跳过对快捷节点快捷样式的生效。

但是,也有一些缺陷,刚才说的支持的 Selection 有限制,所以我们设置下划线还是无法实现,只能做纯透明的标记。

现在 Highlight 还不支持绑定事件,因此无法绑定典型事件说明哪个节点被激活了。

评论当中还是有一个功能,就是行评统计,这样的情况下 Highlight 也是无解,它还是要重新计算遍,通过矩形交叉判定位置。这些就是 Highlight API 当中最消耗性能的部分,它根本无法跳过这些步骤,所以性能和之前几乎是一致的。

所以,实践下来,我们发现它的表现力还是不够,现在支持的 CSS 样式还是太少了。第二,块级元素还是无法处理,还是要基于块级元素自行高亮的逻辑,像选中、协同模式下的块锁定。第三,高亮的选区目前无法绑定事件。第四类似行评统计需求还是需要选区自行实现。

目前 Highlight 变更后是否需要同步触发渲染等问题还在讨论当中。

Highlight 目前草案阶段的实现还是只用于文本搜索场景,对语法错误标记等无交互场景下文本选区高亮问题,期待大家可以参与到 Web 文本编辑器相关的讨论当中来,用 Highlight API 规范能够尽快落地,让开发者可以尽快实现 Highlight API实现边界中的选区高亮效果。

谢谢大家!

提问:同学,您好!我有一个问题,因为我是视障人士,目前在线文档编辑方面,支持都不太理想,我希望后续能够有机会尽量适配在线的协同编辑文档,也能够让残障人士特别是视障人士参与工作的过程中不用再麻烦其他的同事进行一些编辑或者其他。谢谢!

云澹:关于无障碍,我们确实考虑过这个问题。目前能做得比较明显的情况下,就是在阅读障碍的情况下,通过普遍的阅读器可以让视障人士更好的阅读,现在语雀可以实现无鼠标操作,可以纯键盘操作,基于这样的方案设计下,后续可以在键盘、光标的过程中提供语音播放,让你独自通过键盘完成基本的写字。

提问:这个思路挺好。因为刚才您说到一些快捷键,有可以参考一下微软在word当中视障人士可以使用文档,后续看看如何在网页端实现。刚开始可以让人能够阅读文档,然后进行一些文字的基础修改。就是这样,不知道这些好不好实现?

云澹:非常感谢你的建议,会调研一下,争取满足你在文档读和写方面的需求。


返回[会议总结页面]获取其他话题的会议纪要。

若您对上述内容有任何疑问或需进一步协助,请联系:会议主办方 W3C 北航总部 <team-beihang-events@w3.org>。