H5 Dooring 可视化编辑器源码分析

2,512 阅读7分钟

目录

  • 一、UI设计
  • 二、项目分析
  • 三、功能分析
  • 四、组件分析
  • 五、数据结构
  • 六、交互流程
  • 七、第三方依赖
  • 八、关键技术点
  • 九、总结

一、UI设计

二、项目分析

项目主要有两部分,一个是可视化编辑器(editor),一个是预览(preview)。使用了lerna包管理多个项目。

1、Editor 编辑器

编辑器可视化工作区域,使用了Preview预览editor模式。

动态表单模块,包括表单项

2、Preview 预览

预览主要承载两个功能,一个是view模式,一个是editor模式。

UI组件库

三、功能分析

头部功能区域包括:

  • 模版库 -- 未实现
  • 保存 -- 主要保存两份数据,组件数据(pointData)
  • 导入/导出 -- pointData
  • 复制/粘贴/重做/撤销/删除 -- 操作pointData
  • 预览 -- 打开Preview

左侧组件库区域:

  • 组件类别 (category),eg:基础类别、多媒体、图表
  • 每个类别下面,还包含组件类型 (type),eg:文本、图片、...

中间编辑区域:

  • 工作区 -- 可以拖动

  • 上刻度、左刻度

  • 画布 -- 页面制作白色区域,组件布局

  • 控制条 (toolbar)-- 主要是缩放

右侧属性区域:

  • 组件属性表单

四、组件分析

1、Editor 容器组件

编辑器布局,上面Header、下面分左边组件库、中间区域工作画布、右边属性表单。布局容器里面用到React DND来处理拖放,Draggable 来处理布局工作画布拖动,缩放。

2、Header 功能组件

功能区,包括保存、导出、导入、预览、复制、粘贴、撤销、重做等。

3、 TabPane 组件库面板

组件库面板,所有组件列表存放的地方。可拖动的组件都放在这个区域。

使用到了第三方库Antd的Tabs。

4、SourceBox 组件

拖拽源容器,所有的组件都需要这个容器包裹,才能具备拖拽能力。实际上组件库渲染的时候已经全部被该容器包裹。

使用了第三方库React DND

5、TargetBox 组件

拖放源容器,接收拖拽源的组件数据,调用ViewRender渲染到画布上。

使用了第三方库React DND

使用了Draggable使整个容器可在工作区域随意拖动。

6、CanvasControl 组件

控制工作画布放大缩小

7、MenuProvider右键菜单组件

为工作画布区域的组件提供右键菜单。使用第三方库react-contexify。

8、Calibration 刻度组件

为工作区域提供尺寸参考。

9、FormRender 动态表单组件

右边属性区域,渲染组件的属性,可编辑。主要基于Antd的Form组件实现。

10、FormItem 表单组件库

包含各种类型的表单组件,图片、文本、颜色、输入框等。

11、ViewRender 渲染组件

预览界面的容器,最外层是一个可拖拽的盒子组件(第三方react-grid-layout组件),里面循环渲染所有的DynamicEngine组件,循环的数据就是PointData。

12、DynamicEngine 组件

这个是实现动态渲染的组件,主要使用Umijs的dynamic动态加载UI组件功能。输入PointDataItem数据,根据类型(type),类别(category)查找到需要渲染的组件目录,动态import组件。

13、UI组件库

组件分不同的类别(category),每个类别下面有不同类型(type)的组件。比如:

  • base 基础组件,图片、输入框、文本等
  • visual可视化组件,饼图、折线图、柱状图等
  • media多媒体,视频、音频等
  • 其他自定义的组件

五、数据结构

1、UI组件数据结构 (template, schema)

定义一个UI组件,必须包含template定义、schema定义两部分数据。

  • template 组件模板定义,主要是在编辑器组件列表里面显示
{
  // 类型
  type: 'Footer',
  // 网格规则的坐标,主要用在React Grid Layout的布局h属性
  h: 10,
  // 组件库显示的名称
  displayName: '页脚组件',
  // 组件类别
  category: 'base',
  // 网格规则的坐标,主要用在React Grid Layout的布局x属性
  x?: 0,
}
  • schema 组件schema定义,包含可编辑属性(editData)、props属性配置(config)
// schema 对象
{
  // 可编辑属性列表
	editData: [ ... ],
  // 可编辑属性默认值           
  config: { ... }
}

// editData item
{
  // 属性key,主要设置在css
  key: 'bgColor',
  // 属性名称,显示在动态表单label
  name: '背景色',
  // 表单组件类型
  type: 'Color',
}

2、布局数据结构(point data)

ViewRender组件主要遍历pointData,调用DynamicEngine来动态渲染组件。这是一个数组,单个项结构如下:

{ 
  id: 'string',
  // React Grid Layout网格规则, 布局用
  point: { x: number, y: number,  w: number, h: number, ... },
  // 组件拖拽数据源
  item: {
    category: string,
    type: string,
    // 右侧动态表单的数据,主要是组件的外观、布局属性,用的都是style
    config: json
    ...
  },
  // 布局状态,暂时没搞清除有什么用
  status: 'inToCanvas'
}

3、组件拖拽数据源 (source data)

从组件库拖拽一个组件的时候,数据源如下,拖放区域可以接收到这部分数据。

{
  // 组件类型,模版配置下面的type
  type: template.type,
  // 组件默认值
  config: schema.config,
  // 网格规则的坐标,主要用在React Grid Layout的布局h属性
  h: template.h,
  // 属性列表
  editableEl: schema.editData,
  // 组件类型
  category: template.category,
  // 网格规则的坐标,主要用在React Grid Layout的布局x属性, 很多组件是没有配置这个属性
  x: template.x || 0,
}

六、交互流程

1、组件面板初始化

加载组件配置(template) -》map遍历渲染组件,渲染组件用到DynamicEngine,具体见预览流程说明

2、数据处理过程

组件源数据(template,schema)-》经过拖拽封装成拖拽数据源(source)-》Dispatch到状态管理生成pointData

3、可视化设计流程

渲染流程,走预览一样的逻辑。编辑区域交互过程:

左边组件库拖动源组件(Source) -》工作画布(Target目标区域)监听拖放事件,接收到源组件数据 -》调用ViewRender渲染组件到工作画布 -》右边属性区域加载目标组件的属性

  • SourceBox 组件用到React DND包装成拖拽源
  • TargetBox 组件也用到React DND包装成接收拖放源
  • 工作画布监听到PointData,调用ViewRender渲染目标组件
  • 数据面板加载当前目标组件的属性

4、组件属性编辑渲染流程(FormRender)

  1. 从state取到当前组件数据currPoint,读取属性editData表单配置,config默认数据

  2. 调用FormRender,传入editData,config

  3. 根据editData的key,name,type,生成对应的表单类型,并初始化默认值

  4. 监听表单的onFinish事件,获取values,dispatch到state修改得到新的point data

  5. ViewRender监听到point data变化,重新渲染可视化设计区域

5、渲染流程

  1. 输入pointData,pageData
  2. ViewRender容器组件开始创建布局容器ReactGrid,并根据pageData设置页面外观
  3. 遍历pointData
  4. 调用DynamicEngine,输入pointDataItem,动态加载UI组件
  5. 渲染UI组件到ReactGrid布局容器
  6. 完成

七、第三方依赖

  • Umijs
  • Antd
  • React DND
  • Draggable
  • React Grid Layout
  • React Contexify
  • Redux Undo

八、关键技术点

1、组件设计

目前,这个框架组件设计还相对简单。定义属性、默认值还有组件简单实现。

2、动态表单

动态表单基于Antd的Form实现。

3、拖放功能

拖放功能主要基于React DND实现。

4、动态加载

基于Umijs的Dynamic动态加载组件实现。

5、组件布局

基于React Grid Layout的布局。

九、总结

技术实现不难,简单明了,结构清晰。对于一般的推广活动页面,主要以展示为主,没有太多交互的页面,足够应付了。

附录:

开源项目地址:github.com/MrXujiang/h…

根据自己的使用情况,我对这个开源项目做了一些修改,下面是我的项目地址(仅供参考学习):gitee.com/mokesun/h5-…