概述
云凤蝶是一个 hpaPaaS,目标是为中后台应用研发提供简单、高效、高质量的研发方案。在云凤蝶的自由画布、属性面板和数据驱动等特性的支持下,不仅是前后端,甚至 PD、设计师都能直接参与研发工作。
这篇文章就来分享下,云凤蝶的自由画布之道。
在云凤蝶的自由画布里,一切都是分层的。最底下是诸如 antd 等画布组件的渲染层,往上是错误报告层、框选层、右键菜单层、对齐线层、间距线层、选中/锁定边框等等非常多的交互层。
例如,画布的交叉错误红色警告、参考线、标尺:
选中/锁定边框:
右键菜单:
直观上看,交互层是这样的结构:
这种结构是云凤蝶自由画布迭代了无数遍之后沉淀下来的。它把画布从顶层划分成了渲染和交互两个方面。渲染层只负责把组件渲染到画布上,而交互层才是真正承载用户操作的地方,实现复杂业务功能的分层治理。
渲染层
云凤蝶画布的渲染层只负责组件渲染一件事,它不涉及用户操作。
画布组件都由 HOC 高阶组件包裹过,这个高阶组件做两件事情:
-
-
交互层
云凤蝶画布的交互层真正承载了与用户的交互,比如错误报告、参考线等信息展示,框选、右键菜单等点选操作。
PositionMonitor 和 EventBus
真实业务场景里,用户和画布会产生非常多非常细的交互过程:
-
-
-
组件所在位置上要渲染参考线、报错信息等,而且拖拽组件的时候信息能跟随拖拽
-
基本所有的交互都依赖一些关键信息:
-
-
用户在组件上产生的 event 事件,比如 MouseEvent、DragEvent 等
而画布的交互层抓取到这些信息后,就可以做相应的操作了。这实际上是个渲染层和交互层通信和状态共享的过程,所以画布设计了 PositionMonitor 和 EventBus 来处理,如下图所示:
EventBus 是个经典的事件总线。用户的交互事件默认作用到了画布组件渲染层。渲染层通过 EventBus 抛出事件给交互层。交互层接到事件后进行自己的业务操作。
PositionMonitor:基于浏览器 MutationObserver 的画布组件坐标收集机制。当画布组件位置变化时,就会触发 PositionMonitor 调用 getBoundingClientRect 方法,收集组件坐标。
可观察模型驱动视图
画布的交互性要求非常高,各种交互状态要及时响应。比如组件拖拽的时候,对齐线、间距线、等距线等都要高性能实时绘制,否则会产生拖拽卡顿感。
React 经典的浅比较 props 自顶向下更新虚拟 dom 的方式已经不太能满足画布复杂状态下也要保持高性能的需求,所以画布组件内部同样采用了云凤蝶可观测 DDD 充血模型驱动视图的机制。
“
云凤蝶的模型驱动视图机制:定义一个可观测的 DDD 充血模型,当模型数据改变时,相应的 React 组件自动 forceUpdate 更新视图。这种方式可以让 UI 视图精确响应数据变化更新。
工程实现上,每个层都可以对画布暴露可观察的 Model,然后画布会把这些 Model 引用统一挂到内部的 $model 属性上,架构就变成如下图所示:
$model 是画布的顶部属性,共享给了所有画布层,完成了两件事情:
-
-
只要画布层代码上访问了
model 的字段,数据变化自然而然精确触发相应画布层的视图更新,而没有访问 $model 的画布层则不更新,避免性能损耗。
UI 视图堆叠模式
通常情况下,交互层视图会 100% 盖在渲染层上面,然后通过读取组件坐标来定点绘制交互所需要的信息。例如间距线、对齐线、框选线等。直观上看大概是这样:
这种视图堆叠模式能解决绝大部分的视觉需求,但为了达到更极致的视觉表现力,单一的视图堆叠模式还不够:
-
组件坐标收集过程有一定的延时。如果交互视图强依赖了组件坐标,那也会产生延时,直观上的体验就是交互视图跟随不流畅。
-
交互视图会始终覆盖在组件渲染层上方,做不到画布组件盖住交互视图的效果。例如,选中的组件外围要有蓝色的边框,而边框却被另一个组件盖在了下面,如下图所示。
为了达到更极致的视觉表现力,画布目前一共设计了 4 种视图堆叠模式:Layer、InstanceLayer、WrapperLayer 和 ToolBar,整体结构如下图所示。它们分别实现了交互视图的画布覆盖、组件覆盖、画布包裹、工具条展示功能。只要通过适当的组合,基本上可以实现任意复杂度的视觉需求。
在工程实现上,一个交互层可以同时应用多个视图堆叠模式:
结语
本文主要介绍了云凤蝶自由画布的分层模型。在分层的视角下,组件渲染和各种交互操作都分门别类依次码放在不同的画布层,达到了较好的维护性和较高的性能。
自由画布是低代码建站的重要武器,未来还将开放一键摆放、智能布局等更加傻瓜化的页面搭建能力。
未来已来,时不我待!
云凤蝶招聘前端、Java、PD、设计岗位,未来等你共创!
如果你感兴趣,欢迎联系 [email protected] 或 [email protected]