Skip to content

Commit

Permalink
25/02/28
Browse files Browse the repository at this point in the history
  • Loading branch information
WindRunnerMax committed Feb 28, 2025
1 parent 355ec32 commit 61a5e0c
Showing 1 changed file with 36 additions and 4 deletions.
40 changes: 36 additions & 4 deletions RichText/初探富文本之文档虚拟滚动.md
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,9 @@ class CommentModule {
}
```

此外,通常我们的评论还会存在跳转到上一处/下一处的功能,那么设想一下,此时由于虚拟滚动的存在我们不能够直接通过`DOM`节点的位置来获取其距离顶部的`TOP`值,也就没有办法正确将其按照顺序排序,即我们的上一处/下一处跳转功能可能会存在乱序跳转的情况。那么对于这个问题,我们还需要关注到编辑器本身块结构的偏序问题,即任意两个节点都是可以比较的,这个能力同样依赖我们的块级管理能力,通常来说如果是`Blocks`的编辑器会方便实现,非`Blocks`的编辑器在处理增量插入内部块时就需要额外地计算位置信息。当然即使是`Blocks`的设计也需要初始化以及处理增量计算。此外,当我们建设好这部分能力后,针对于`Effect HOC`的处理也会方便很多,我们可以直接取当前块之后序列的块即可。
此外,通常我们的评论还会存在跳转到上一处/下一处的功能,那么设想一下,此时由于虚拟滚动的存在我们不能够直接通过`DOM`节点的位置来获取其距离顶部的`TOP`值,也就没有办法正确将其按照顺序排序,即我们的上一处/下一处跳转功能可能会存在乱序跳转的情况。那么对于这个问题,我们还需要关注到编辑器本身块结构的偏序问题,即任意两个节点都是可以比较的,这个能力同样依赖我们的块级管理能力。

通常来说如果是基于`Blocks`设计的编辑器会方便实现,非`Blocks`的编辑器在处理增量插入内部块时就需要额外地计算位置信息。当然即使是`Blocks`的设计也需要初始化以及处理增量计算。此外,当我们建设好这部分能力后,针对于`Effect HOC`的处理也会方便很多,我们可以直接取当前块之后序列的块即可。


```js
Expand All @@ -750,7 +752,21 @@ list.sort((a, b) => {
});
```

在这里的块之间的比较实际上如果仅仅是维持了整个树状的层级结构是不够的,我们还需要记录其在当前嵌套结构中的索引值,这在`Blocks`的结构中很容易实现,只需要取得其父级的`children`中`index`进行比较即可,然而在非`Blocks`的嵌套结构中,我们就需要记录其本身的序列号,这个序列号可以是完整的`index`,也可以是仅仅表达先后顺序的`op-serial`。而我们实际比较的时候,除了块与块之间的比较,还需要比较`op`与`block`之间的顺序,例如某个块中的`op-a`与嵌套结构`block-op-b`的比较,就必须要提升到同级比较,而且所有的块比较也都需要提升到同级或者同父级比较才可以正确取得其顺序。此外,在我们的块管理器结构中,进行`serial`的更新也是必不可少的,在这里我们通常只需要关注其名义上的序列号即可,而且只需要更新编辑时选区所在的块级`serial`就足以表达我们的需要的平级顺序关系了。
那么我们以`Tabs`为例描述一下问题所在,此时存在`A``B`两个`Tab`,每个`Tab`中都存在评论内容。假设现在活跃的`Tab``A`,此时评论`1`的内容的`top`值是比较大的,因为此时`B`是折叠状态的,这就导致评论`2`未实际渲染`DOM`导致只能获取可观测的`top`值,因此其值本身更小。那么这种情况下就会导致`2``1`之前,而由于切换评论那么就会导致`B`渲染而`A`折叠,其评论节点渲染顺序就会反过来。

其实关于这个复杂问题我们可以再重新规整一下,由于我们的节点是可以嵌套的,那么以`Tabs`作为`root`的话,在块管理的树结构中就需要存在任意嵌套块节点的比较,因此可以推理出来对于整个文档树都是类似的结构。实际上即使不是纯`Block`的结构,而是其中存在`Line`的状态作为中间态的话,也是可以采用类似的方案处理,因为其本质上还是树形结构。

```
Tabs
/ \
Block Block
/ \ / \
Block Block Block Block
```

在这里的块之间的比较实际上,如果仅仅是维持了整个树状的层级结构是不够的,我们还需要记录其在当前嵌套结构中的索引值,这在`Blocks`的结构中很容易实现,只需要取得其父级的`children``index`进行比较即可。然而在非`Blocks`的嵌套结构中,我们就需要记录其本身的序列号,这个序列号可以是完整的`index`,也可以是仅仅表达先后顺序的`op-serial`

而我们实际比较的时候,除了块与块之间的比较,还需要比较`op``block`之间的顺序,例如某个块中的`op-a`与嵌套结构`block-op-b`的比较,就必须要提升到同级比较,而且所有的块比较也都需要提升到同级或者同父级比较才可以正确取得其顺序。此外,在我们的块管理器结构中,进行`serial`的更新也是必不可少的,在这里我们通常只需要关注其名义上的序列号即可,而且只需要更新编辑时选区所在的块级`serial`就足以表达我们的需要的平级顺序关系了。

```js
class BlockManager {
Expand All @@ -770,7 +786,7 @@ class BlockManager {
} else if (a.level < b.level) {
b = this.elevate(b, diff);
}
// `a/b`所在的`block`是嵌套关系
// a/b 所在的 block 是嵌套关系
if(a.id === b.id){
return a.serial - b.serial;
}
Expand All @@ -787,7 +803,23 @@ class BlockManager {
}
```

在前边我们提到过很多次我们不能通过`smooth`的平滑调度来处理滚动,因为我们需要明确的高度值以及视口锁定调度,那么我们同样可以思考一下这个问题,由于我们相当于完全接管了文档的滚动行为,那么明确的高度值我们只需要将其放置于变量中即可,那么视口锁定的调度的主要问题是我们不能明确地知道此时正在滚动,如果我们能够明确感知到正在滚动话就只需要在滚动结束之后再进行视口锁定的调度与块结构的渲染即可,在滚动的过程中不会调度相关的模块。
其实这里的`serial`我们并不一定需要维护在`BlockManage`中,因为此时我们找到了相同的父级节点,那么我们只需要在`sort`的时候对比两个子节点的顺序。上述获取`level`也可以现场查询,而不必要维护起来,当然也可以空间换时间,在查找时记录`level to state`,然后根据`diff`的值直接取对应的`state`即可。至于后续的父节点查找操作,则并不能够避免,最后则需要关注的是序列比较。

`sort`的方法中,以`a, b`的参数为例,如果`a`在前的话那就相当于保持当前顺序,应该返回`-1`,如果是`b`在前的话就相当于交换顺序,应该返回`1`。通常情况下这里的值不应该返回`0`,但是我们可以使用`top`的值进行兜底。需要注意,我们的策略相当于根据渲染顺序来决定评论顺序,而`top`值仅在`block id`相同的情况下才会作为比较值,即在面板中需要实现评论顺序固定且`Top`动态变化。

```js
sort((a, b) => {
// ...
const common = a.parent;
for(const child of common.children) {
if(child.id === a.id) return -1;
if(child.id === b.id) return 1;
}
return a.top - b.top;
})
```

此外,在前边我们提到过很多次我们不能通过`smooth`的平滑调度来处理滚动,因为我们需要明确的高度值以及视口锁定调度,那么我们同样可以思考一下这个问题,由于我们相当于完全接管了文档的滚动行为,那么明确的高度值我们只需要将其放置于变量中即可,那么视口锁定的调度的主要问题是我们不能明确地知道此时正在滚动,如果我们能够明确感知到正在滚动话就只需要在滚动结束之后再进行视口锁定的调度与块结构的渲染即可,在滚动的过程中不会调度相关的模块。

那么关于这个问题我有个实现思路,只是还没有具体实施,既然我们的滚动主要是为了解决上边两个问题,那么我们完全可以模拟这个滚动动画,也就是说对于固定的滚动`delta`值,我们根据计算模拟动画效果,类似于`transition ease`动画效果,通过`Promise.all`来管理所有的滚动进度,紧接着通过队列实现后续的调度效果,当需要取得当前状态时通过滚动模块决定取调度值还是`scrollTop`,当滚动完成之后再调度下一个任务。当然实际上我觉得这个方案可以作为后续的优化方向,即使是我们不调度动画效果,通过定位到相关位置实现目标闪烁的效果也是不错的。

Expand Down

0 comments on commit 61a5e0c

Please sign in to comment.