简介
前一章节确定了 G6 的范围,本章对一些细节进行讨论,主要对下面几个领域进行设计:
- 数据结构
- 事件、行为和模式
- 自定义节点和边
- 动画机制
- 类结构
其他的诸如数据流程、接口定义、具体支持哪些交互,在详细设计中阐述。
数据结构
node
默认提供的类型
- G中提供的基础图形
- 锚点机制
{
id: 'node1',
color: '#333',
className: '', // 标识符
size: 10 || [10, 10], // [width, height]
shape: 'circle', // node类型
style: { // keyShape的绘图属性
fill: 'red',
stroke: 'blue'
},
x: 0, // x坐标
y: 0, // y坐标
label: '文本标签',
labelStyle: {
fill: 'green'
},
}
-
edge
默认提供类型
直线line
- 贝塞尔曲线
- step curve
- polyline
-
扩展机制
edge cfg
边的要素有:form, color, size, label。对应如下结构:
{
id: 'edge1',
source: 'node1',
target: 'node2',
sourceAnchor: 1, // 可选
targetAnchor: 0, //可选
controlPoints: [{
x: 10,
y: 10
}],
color: 'red',
size: 3,
shape: 'line',
style: {
fill: 'red',
stroke: 'blue',
startArrow: boolean | fn,
endArrow: boolean | fn
},
label: '文本标签',
labelStyle: {
fill: 'green'
},
}
-
事件、行为和模式
考虑到 G6 需要同时支持编辑器和分析场景,同样的用户行为会导致不同的结果,其出发点也不同,考虑几个典型场景:
编辑器中拖拽节点位置,相邻的边重新计算;分析场景下拖拽单个节点,由于存在力导布局算法,所有的边和节点都会受到影响,进行移动。
- 编辑器中框选选中多个节点,进行高亮,一些命令按钮高亮;分析场景下使用不规则图形框选多个节点,上面生成蒙层,可以拖动蒙层,其他图表与之进行联动。
- 同样的拖拽,拖拽画布、拖拽节点、拖拽边、拖拽分组,都有不同的反馈和结果。
- 相同的行为,例如拖拽,在编辑图的场景下和查看图的场景下,用户的目的也不一样,可以做的事情也不一样。
所以 G6 依然会使用事件、行为和模式的设计:
- 事件:画布支持的事件,包括图形事件、组件事件、自定义的事件
- 状态:画布、数据的状态,状态的改变会触发事件,有必要对这些状态进行细分。
- 行为:用户在图表上的行为,例如鼠标在图表上移动、框选、拖拽、点击等整个的操作过程,每种行为都带有目的
- 反馈:用户在图表上进行交互的反馈
- 模式:查看视图、编辑视图、移动视图
事件
G6 层次的事件主要分为:
- 图形事件:click、mousemove 等
- 画布事件:resize、zoom 等
- 节点、边等元素的事件:add、update、remove、drag 等
-
行为
仅仅触发事件,没有任何意义,事件(包括状态改变) + 目的 = 行为,所以行为是带有目的的事件,行为往往是连贯的,用户框选元素时元素被选中(选中行为),用户按下删除键,元素被移除(删除行为)。行为的目的要同行为的结果一致,否则就是无效行为,甚至是 Bug。
基本行为
G6 层次支持的行为:
hover/active 元素
- 点击选中节点,shift 多选
- 框选节点
- 画布拖拽
- 画布缩放
- 拖拽节点、拖拽边(不考虑改变链接)
G6-Editor 和 G6-Analyzer 支持的行为不在这里列出。
反馈
行为过程中画布的响应:
- 行为开始,伴随触发行为的事件的鼠标、画布、节点的变化,例如框选时,按下鼠标,鼠标的形状发生变化
- 行为过程中,框选拖拽的过程中,出现虚框
-
模式
为什么要使用模式对用户的交互进行区分,考虑一下场景:
用户在空白画布上拖拽鼠标,是进行拖拽画布还是框选?
- 用户拖拽节点的边,是 resize 还是出现连接线?
- 用户滚动鼠标滚轮,是放大还是滚动条移动?
- 用户双击节点,是弹出 Dialog 还是编辑文本?
所以模式是为了区分相同事件(用户操作)产生不同反馈的行为而设计的,同一个模式下行为的结果是确定的,不能一个在画布空白处的拖拽既是框选又是拖动画布。
我们按照对 G6 的划分对模式进行梳理:
- G6 通用的场景(编辑和分析都支持)
- G6-Editor 编辑场景的模式
- G6-Analyzer 分析场景的模式
通用场景
注意点:
- 一个模式下有多种行为,一种行为可以在多个模式下生效
- 模式和行为不进行绑定,可以给任意模式增加任意行为,保证不冲突即可
- 可以自定义模式和行为
自定义节点和边
G6 中对节点和边的定义最基本的类型,在数据结构中的 shape 字段类来确定使用什么类型的节点或者边。
默认的节点和边
默认的节点有:
- 简单图形 + 文本, circle, rect
- 图片 + 文本,image,需要传入图片地址
- 列表:title + name + value 的列表
默认的边:
- 直线(line),节点与节点的中心连接线,可能被节点的边框截断
- 直角线(step),有垂直和水平方向的线段组成的连线
- 折线(polyLine),多条线段组成的连线
- 曲线(curve),贝塞尔曲线、平滑曲线
线上的文本比较复杂,有些文本在线上居中,有些文本分布在线的两端,而且线还要同线同向
自定义
节点和边的自定义主要是基于内置的节点和边进行扩充,两种元素的扩展有很大的差别:
- 自定义节点,需要解决绘制节点和更新节点的问题
- 自定义边,边如何同节点进行连接,边的更新,边控制点如何影响边的形状
节点和边的 active、selected 的效果也可以考虑在这时候实现
更新机制
节点和边的更新差异也比较大:
- 节点在以下场景下会触发更新:
- 移动
- 数据更新
- 刷新
- 移动
- 边的更新
- 端点发生变化(端点变动、节点移动)
- 控制点、锚点发生变化
- 数据更新
- 端点发生变化(端点变动、节点移动)
在之前的版本中节点和边的更新都是直接删除重新绘制,需要重新思考这两种元素的更新方式。
动画机制
主要考虑两种情况下的动画:
- 初始化时的动画:大多数力导布局都是渐进的,所以需要不断的调整节点位置,位置之间需要补间动画。
- 交互过程中:树节点的展开、折叠,力导布局后的节点被拖拽
这两种动画都与布局算法相关,所以需要思考所有节点刷新时的操作接口和最佳实践。
还有其他的动画需要考虑
- 节点激活时,出现光环效果
- 边上的动画,表示数据流向
- 节点上有些图标进行动画,表示节点的状态
这些动画都同图形相关,所以需要在定义节点时处理。
类结构
根据上面的讨论列出 G6 的类结构