css3文字环绕-【第2203期】有道云笔记新编辑器架构设计(上)

编辑是指如何提供一个编辑区域供用户在编辑区域中编辑文档,以及如何感知用户编辑区域中的编辑动作并通知控制器更改数据模型。 浏览器提供 contentEditable 属性以使元素可编辑。 大多数编辑器都是基于这种思想进行编辑的,他们可以拦截contentEditable元素的干扰并将干扰通知控制器。 还有一些编辑器实现了自己的编辑区域和事件系统,例如 Google Docs。

操作说明:

是指控制器根据接收到的编辑区域的编辑动作,生成相应的指令来改变显存模型,从而更新显存模型,完成循环。 这部分与数据模型相关。 如果数据模型是HTML,编辑器可以通过execCommand直接改变HTML数据。 如果是自定义数据,在编辑区域监听或者拦截风暴,还可以推断意图生成改变数据模型的指令。 。 通过指令改变数据,可以更方便地实现撤消和重做、历史版本恢复、协同编辑等功能。

1.4 基于浏览器的富文本编辑器的技术变化

基于以上四个问题,基于浏览器的富文本编辑器可以定义为四代:

第一代:

完全基于浏览器API设计,数据模型直接使用HTML数据,渲染使用原生HTML,编辑区域使用contentEditable生成,浏览器自身改变HTML数据的指令通过execCommand执行。

这类编辑器通常出现在《XX行代码教你实现一个富文本编辑器》之类的各种博客中。 基本上没有成熟的开源编辑器或者商业编辑器采用这种设计方式。 他们的主要问题在于 execCommand 接口:

仅提供有限数量的命令。 例如css3文字环绕,execCommand 无法支持插入待办事项列表。 提供的某些命令的功能有限。 例如,'fontSize'命令只能支持1-7,因此无法自定义字体大小。

修改的结果取决于浏览器。 例如,“粗体”命令会在某些浏览器上为所选内容中的文本添加标签,而在其他浏览器上则会添加标签。

第二代:

由于execCommand的功能限制,第二代编辑器一般放弃使用浏览器的execCommand接口直接更改HTML文档的方法。 相反,他们使用自己实现的 execCommand 和指令来更改 HTML 文档。 通过这种方式,他们可以获得更大的灵活性。 各种功能。

这种类型的编辑器的主要问题是不同的 HTML 结构可能意味着相同的事情。 例如,下面的两行 HTML 都代表一段粗体和斜体的文本,但它们的 HTML 结构不同,因此很难比较数据是否相同。

第三代:

针对HTML含义不一致的问题,第三代编辑器放弃了同时使用HTML作为文档模型和渲染的策略,转而采用自定义的数据模型,例如XML数据模型或JSON数据模型。 渲染相同的数据模型生成的HTML是相同的,自定义操作可以保证相同的操作改变后文档模型是相同的。

环绕文字怎么设置_css3文字环绕_环绕文字是灰色

目前常见的编辑器产品如有道云笔记、Graphite Document等,以及开源编辑器库如Slate、Draft、Quill等,都属于第三代编辑器,已经可以满足大部分应用场景。 但由于渲染页面中的可编辑区域仍然基于contentEditable,因此需要根据截获的storm判断用户行为,并生成相应的指令来改变数据模型。 一旦用户数据没有被拦截,或者处理行为不正确,用户的行为可能会直接改变contentEditable元素,导致数据和视图不一致。 因此形成的Bug很难定位和修复,往往出现在编辑器的移动适配中。

第四代:

为了解决contentEditable引发的不可控争议,以Google Docs为代表的第四代编辑器彻底抛弃了contentEditable,实现了自己的排版引擎。 排版引擎控制文档的页面和布局,并将数据渲染为页面上的 HTML。 同时,由于contentEditable被废弃,必须解决诸如概述光标和选择、监视文本输入事件等技术问题,才能实现类似于浏览器的编辑体验。

与第三代相比,第四代编辑器的优势在于彻底解决了contentEditable带来的bug,并且具有更好的可扩展性。 相应的代价就是开发比较困难,体验不如原生,还可能遇到性能问题。 目前,只有 Google Docs 等使用这些框架来开发编辑器。

2、新版云笔记编辑器技术选型

根据上一节介绍的实现富文本编辑器的四个要素,我们总结了四代编辑器的技术选型,如下表所示:

新版有道云笔记编辑器结合项目的可扩展性和实施难度,做出了以下技术选型:

环绕文字是灰色_环绕文字怎么设置_css3文字环绕

2.1 模型

新版有道云笔记编辑器采用定制的JSON数据格式作为内存模型。 存储模型与显存模型基本对应,是显存模型的压缩版本,可以减少数据序列化和反序列化时的错误。

文档模型:

新编辑器的显存模型采用文档-段落-文本的三层模型。 顶层对象是文档(下图中的灰色区域)。 一个文档包含多个段落(下图中的灰色区域)。 每个段落至少有一个文本(下面的灰色区域)。

对于三层文档模型,我们自然可以想到用树结构来表示,如下图所示:

由于JSON格式可以自然地表示嵌套的树结构,因此我们的三层文档模型可以表示为以下JSON结构:

环绕文字是灰色_css3文字环绕_环绕文字怎么设置

富文本意味着:

对于富文本编辑器的数据模型,需要考虑文本的行内样式和段落样式:

内联样式是应用于文本的样式。 每个文本可能有不同的行内样式,例如宋体、斜体、文本颜色、背景颜色、字体、字号等。

段落样式用于段落样式,整个文本只会有一种段落样式,例如对齐方式、行高、段落缩进等。

由于我们的三层文档模型中的段落是一个单独的层,有相应的段落节点,因此对于段落样式,我们只需要在段落节点上添加一个表示段落样式的数组即可。 我们向段落添加了数据字段css3文字环绕,并为其添加了样式属性。 表示段落的段落样式,如下图所示:

如果要表达内联样式,目前还没有办法在文本节点中只保存一个内容字段。 需要将其一一拆分为字符,并为每个字符添加一个表示内联样式的数组。 比如我们可以使用一个chars数组,里面的每个元素代表一个字符,text字段代表该字符的内容,marks数组代表该字符的内联样式。 下图表示富文本。

叶节点:

环绕文字是灰色_环绕文字怎么设置_css3文字环绕

在上面的富文本行内样式表示方法中,我们可以看到,富文本行内样式表示方法中,r、i、c、h这四个字符上保存了富样式宋体、斜体、红色背景色,存在冗余数据。 我们参考HTML渲染结果以及一些开源编辑器的实现来定义合并规则。

如果连续的字符节点具有完全相同的内联样式,则可以将它们合并为叶节点。

例如,在前面的示例中,四个字符 r、i、c、h 是连续的并且具有完全相同的内联样式。 它们可以合并成叶节点。 类似的“a”和“text”也可以合并为叶子节点,因此我们可以将文本节点简化为:

综上所述,支持内联样式的简化文本节点也是一种树形结构,其中包含一个或多个叶子节点。 每个叶子节点都包含文本的内容以及内容相同的内联样式,并且相邻叶子节点的内联样式不能完全相同,如下图所示:

2.2 渲染

新版云笔记编辑器仍然使用浏览器进行排版和渲染。 它没有像Google Docs那样自行开发的排版引擎。 原因是我们觉得浏览器的排版引擎足够强大,基本可以满足日常的文本编辑需求。 只有图片之类的文字,换行、分页、分栏等中间功能很难实现,而且自研的排版引擎需要大量的开发和测试工作量,还可能会带来性能问题,所以暂时,我们仍然使用浏览器进行排版和渲染。

我们采用了React框架,并采用组件化的方式来渲染数据模型。 针对文档-段落-文本的三层数据模型,以及文本节点的叶子模型,我们设计了相应的React组件嵌套进行渲染,如下图所示:

对于叶子节点中包含的文本片段和内联样式,只需要渲染成带有style属性的标签即可。 这也印证了我们设计的富文本模型,简化了富文本的渲染逻辑,让富文本渲染代码显得特别轻量。