diff --git "a/Backup/\344\273\216\351\233\266\350\256\276\350\256\241\345\256\236\347\216\260\345\257\214\346\226\207\346\234\254\347\274\226\350\276\221\345\231\250.md" "b/Backup/\344\273\216\351\233\266\350\256\276\350\256\241\345\256\236\347\216\260\345\257\214\346\226\207\346\234\254\347\274\226\350\276\221\345\231\250.md" index bc8bf1d..de37f82 100644 --- "a/Backup/\344\273\216\351\233\266\350\256\276\350\256\241\345\256\236\347\216\260\345\257\214\346\226\207\346\234\254\347\274\226\350\276\221\345\231\250.md" +++ "b/Backup/\344\273\216\351\233\266\350\256\276\350\256\241\345\256\236\347\216\260\345\257\214\346\226\207\346\234\254\347\274\226\350\276\221\345\231\250.md" @@ -9,9 +9,11 @@ 我也算是比较关注于各类富文本编辑器的实现,包括在各个站点上的编辑器实现文章我也会看。但是我发现这其中极少有讲富文本编辑器的底层设计,绝大多数都是将的应用层,例如如何使用编辑器引擎实现某某功能等。这些应用层的实现本身也会有一定复杂性,但是底层的设计却是更值得探讨的问题。 -而我恰好前段时间都在专注于编辑器的应用层实现,在具体实现的过程中也遇到了很多问题,并且记录了相关文章。然而在应用层实现的过程中,遇到了很多我个人觉得可以优化的地方,特别是在数据结构层面上,希望能够将我的一些想法应用出来。具体来说,主要有下面的几个原因: +而我恰好前段时间都在专注于编辑器的应用层实现,在具体实现的过程中也遇到了很多问题,并且记录了相关文章。然而在应用层实现的过程中,遇到了很多我个人觉得可以优化的地方,特别是在数据结构层面上,希望能够将我的一些想法应用出来。而具体来说,主要有下面的几个原因: ## 编辑器专栏 +纸上得来终觉浅,绝知此事要躬行。 + 我的博客是从`20`年开始写的,记录的内容很多,基本上是想到什么就写什么,毕竟是作为平时学习的记录。然后在`24`年写了比较多的富文本编辑器的文章,主要是整理了平时遇到的问题以及解决方案,集中在应用层的设计上,例如: - [初探富文本之文档虚拟滚动](https://github.com/WindRunnerMax/EveryDay/blob/master/RichText/初探富文本之文档虚拟滚动.md) @@ -48,15 +50,72 @@ ``` -那么从名字上来看,这个零宽字符在视觉上是不显示的,因为其是零宽度。但是在编辑器中,这个字符却是很重要的。简单来说,我们需要这个字符来放置光标,以及做额外的显示效果。需要注意的是我们在这里指的是`ContentEditable`实现的编辑器,如果是自绘光标的编辑器则不一定需要这部分设计。 +那么从名字上来看,这个零宽字符在视觉上是不显示的,因为其是零宽度。但是在编辑器中,这个字符却是很重要的。简单来说,我们需要这个字符来放置光标,以及做额外的显示效果。需要注意的是我们在这里指的是`ContentEditable`实现的编辑器,如果是自绘选区的编辑器则不一定需要这部分设计。 + +我们先来聊一下额外的显示效果,举个例子,我们在选择飞书文档文本内容,如果选中到文本末尾时,会发现末尾会额外多出形似`xxx|`的效果。在平时不关注的话可能会觉得这是编辑器默认行为,但是实际上这个效果无论是`slate`还是`quill`中都是不存在的。 + +实际上这个效果就是使用零宽字符来实现的,在行内容的末尾后面插入零宽字符,就可以做到末尾的文本选中效果。实际上这个效果在`word`中更常见,也就是额外渲染的回车符号。 + +```html +
+
末尾零宽字符 Line 1
+
末尾零宽字符 Line 2
+
末尾纯文本 Line 1
+
末尾纯文本 Line 2
+
+``` + +那么在这个零宽字符如果只是渲染效果的话,那么可能实际上起的作用并不很必要。但是在交互上这个效果却很有用,例如此时我们有`3`行文本,如果此时从第`1`行末尾选到第`2`行时,并且按下`Tab`键,那么此时这两行的内容就会缩进。 + +那么如果没有这个显示效果,此时进行缩进操作,用户可能认为仅仅是选中了第`2`行,但是实际上是选中了`1/2`两行文本。这样的话用户可能会以为是`BUG`,而我们也实际接受过这个交互效果的反馈。 + +```plain +123| +4|x56 +``` + +也对各个在线文档实现进行了简单调研: 基于`contenteditable`实现的编辑器中,飞书文档、早期`EtherPad`存在这个交互实现;自绘选区的编辑器中,钉钉文档存在这个实现;`Canvas`引擎实现的编辑器中,腾讯文档、`Google Doc`存在这个实现。 + +在渲染效果部分,零宽字符还有一个重要的作用是撑起行内容。当我们的行内容为空时,此时这个行`DOM`结构的内容就是空,这就导致此行的高度塌陷为`0`,且无法放置光标。为了解决这个问题,我们可以选择在行内容中插入零宽字符,这样就可以撑起行内容且可以放置光标。当然使用`
`来撑起行高也是可以的,使用这两种方案会各有优劣,且兼容性方面也有所不同。 -我们先来聊一下额外的显示效果, +```html +
+

+
+``` -选区末尾、撑起行内容 +在类似于`Notion`这种块结构的编辑器中,还有个比较重要的交互效果。即块级结构独立选择,例如我们可以直接将整个代码块独立选出来,而不是仅仅能选择其中的文本。这种效果在目前的开源编辑器很少有实现,都是需要自行以块结构重新组织设计选区。 -块级结构选择效果 +通常来说,这个交互同样可以使用零宽字符来实现。因为我们的选区通常是需要放置在文本节点上的,因此我们很容易可以想到,可以在块结构所在行的末尾放置零宽字符,当选区在零宽字符上时就将整个块选中。这里用零宽字符而不是`
的好处是,零宽字符本身就是零宽,不会引起额外的换行。 -inline-block +```html +
+

+    xxx
+  
+ +
+``` + +在结构上,零宽字符还有个非常重要的实现。在编辑器内的`contenteditable=false`节点会存在特殊的表现,在类似于`inline-block`节点中,例如`Mention`节点中,当节点前后没有任何内容时,我们就需要在其前后增加零宽字符,用以放置光标。 + +在下面的例子中,`line-1`是无法将光标放置在`@xxx`内容后的,虽然我们能够将光标放置之前,但此时光标位置是在`line node`上,是不符合我们预期的文本节点的。那么我们就必须要在其后加入零宽字符,在`line-2/3`中我们就可以看到正确的光标放置效果。这里的`0.1px`也是个为了兼容光标的放置的`magic`,没有这个`hack`的话,非同级节点光标同样无法放置在`inline-block`节点后。 + +```html +
+
+ @xxx +
+
+ + @xxx + +
+
+ @xxx +
+
+``` ### 数据结构设计 数据结构设计