> 本文做者:AntV 架构师-萧庆javascript
简介
Antv 过去 5 年中,在不少可视化领域进行了探索,在统计图表、可视分析、关系图、地理可视化等可视化场景中都面临如何把数据转换成图形(可视化编码)的问题,数据一旦以图形的方式呈现给用户,用户则须要在上面进行交互,查看数据细节从不一样的层面对数据进行探查,以一句可视化领域常说的话来总结:“Overview first, zoom and filter, then details-on-demand"。 > 《Visuallization Analysis&Design》是这个领域的一本很是经典的书籍,感兴趣的不能错过。java
G2(统计图表) 、F2(移动端图表)、L7(地理可视化)和 G6(关系可视化) 都在 Overview first 上有了很大的收获,经过可视化编码咱们已经探索了全部常见图表如何从数据转换成图形。从而在 G二、F2 上造成图形语法,在 L7 上使用地理符号学,在 G6上也支持数据映射到节点和边的状态,可是接下来的交互咱们尚未造成统一的理论依据,各自为政。今天咱们在 G2 4.0 上根据过去的经验和教训总结出一套交互语法,初步验证了完备性、易用性和实现交互的效率,咱们将会推广到其余产品上。git
交互回顾
以 G2 为例,咱们在交互方面的探索主要有三个阶段:github
- 内置交互:能够容许的交互都写在代码内部,同渲染、数据更新流程耦合
- 可注册交互:每一个交互一个名称,能够增长配置项用以控制触发和反馈
- 交互语法:将交互分为多个阶段,每一个阶段分为触发和反馈,经过实现常见的反馈,来搭配触发和反馈组合出一套新的交互。
交互内置
图表的交互内置是一种直觉的实现方式,在通用图表的开发过程当中,咱们能够枚举每种图表支持的交互:浏览器
- 全部图表都支持 tooltip
- 图例可以进行数据过滤
- 散点图可使用矩形框框选,折线图能够框选 x 轴
- 饼图点击能够沿着圆心方向向外移动
可是一旦图表库同产品进行结合事情就没那么简单,不少交互同图表相关,可是又与产品的设计相关:框选、过滤、排序和标注等交互须要在不一样的产品中有不一样的触发形式,而交互一旦内置,用户很难改变交互的触发方式和结果。架构
![]() |
![]() |
---|
一旦用户面临交互的改造,咱们的答疑成本会激增,做为图表的开发者也很难改变交互的形态只能在上面增长配置项,打上一个个的补丁。框架
可注册交互
出于交互能够扩展,用户能够自由定制交互的目的,咱们在 G2 v3.四、F2 v3.三、G6 v2.1 版本上增长能够注册的交互,交互再也不同渲染和图表的声明周期进行耦合,依然以 G2 为例:ssh
![]() |
![]() |
---|---|
股票指数走势探索 | brush结合dataset |
![]() |
![]() |
散点图缩放交互 | brush过滤图形 |
其核心思想是:ide
- 开放足够多的事件
- 使用名称来表明一个交互,每种交互支持必定的配置项,使用时仅需一句代码
chart.interaction('brush');
这种方案对于扩展性和用户直接使用已经写好的交互有比较好的支持,可是来定制交互的用户须要深刻理解各个产品的内部细节,例如:框选了画布,有哪些图形会被框选,图形对应的数据有哪些,如何进行过滤。函数
最终的结果是: 这套注册交互的机制,主要仍是图表库(可视化工具)的开发者在使用,不少细节也实现的不够好
更进一步
前面两种交互的实现方式有各类各样的问题,所以咱们在寻找一种易于使用、易于扩展、易于理解的交互模式,由 G2 的图形语法咱们想到了可否创造一套完备的交互语法。
交互语法
图形语法的提示
如何在不影响现有图表声明周期(渲染、更新)的前提下,用户能够自由搭配交互,这看似是个无解的问题,咱们回到 G2 的起点 《The Grammar of Graphics》 ,Leland Wilkinson 将数据转换为图形的过程拆解为:
- 度量:数据各个字段的特征,如何将数据映射到 0-1 空间
- 视觉通道:数据如何用可视化的形式来展现
- 统计函数:汇总统计、回归、密度计算、连接等数据计算
- 坐标系:0-1 的数据如何映射到位置上
- 几何标记:抽象全部图表类型,总结出7种抽象的几何类型
<br>
交互语法的理论依据
回到可视化的交互方面,并无一套成熟的理论,可以提供出足够的抽象,将现有的交互总结到一套框架中。咱们在 唐纳德·A·诺曼 的 《设计心理学》中找到依据,他将交互过程划分为不一样的阶段: <br>同时设计心理学中有 5 个核心的概念:
- 示能:示能指的是物品与人之间的关系,物品的特性与决定物品预设用途的主体的能力之间的关系。
- 意符:意符是一种提示,告诉用户能够采起什么行为,以及应该怎么操做。
- 映射:映射表示两组事物要素之间的关系,一般用在控制与显示的设计上。
- 反馈:交互必须有反馈,告诉用户交互正在执行,同时反馈一样是交互的结果。
- 概念模型:概念模型是高度简化的说明,告诉用户产品如何工做。让用户可以充分的理解交互的整个过程。
可视化的交互语法
综合前面图形语法和设计心理学中的理论依据,咱们对可视化的交互进行分析,总结出交互的目的,同时将可视化交互过程划分为多个交互环节,每一个过程分别有触发和反馈。 <a name="KSxPk"></a>
交互目的
交互的目的也就是用户使用交互的意图,交互意图是否可以知足,是交互设计的根本出发点。在可视化的交互中,用户须要使用交互来完成:
- 数据定义,肯定哪些数据展现,对数据进行操做、加工、探索<br>
- 视图操做,在不一样的视图上操做展现的数据,能够在视图上对数据进行选中、导航、调整数据的展现方式等<br>
- 交互记录,记录交互的轨迹,能够进行交互过程的展现
交互的目的决定: 用户从哪里开始交互,最终的交互结果是什么
<br>对交互目的的详细说明,参看 交互概述
交互过程
咱们将交互聚焦于可视化的场景时,整理了常见的交互后,发现这些交互都有一致的过程,咱们能够将一个交互分解成多个交互环节:
- 示能:表示交互能够进行
- 开始:交互开始
- 持续:交互持续
- 结束:交互结束
- 回滚:取消交互,恢复到原始状态
大多数的交互仅使用上面的部分过程,可是也有些交互须要全部的过程,咱们以一个框选高亮为示例:
- 示能:
- 触发对象:画布
- 触发事件:移动进画布绘图区域、移出画布绘图区域
- 反馈:鼠标形状变成十字、离开时鼠标形状恢复
- 开始:
- 触发对象:画布
- 触发事件:按下鼠标,并滑动鼠标
- 反馈:出现 mask
- 持续 1
- 触发对象:画布
- 触发事件:持续滑动鼠标
- 反馈:mask 随着鼠标变化
- 持续 2
- 触发对象:mask
- 触发事件:mask 的大小变化
- 反馈:被 mask 遮挡的图形高亮
- 结束:
- 触发对象:画布
- 触发事件:鼠标抬起
- 反馈:mask 依然显示,遮挡的图形继续高亮
- 回滚:
- 触发对象:画布
- 触发事件:鼠标双击
- 反馈:mask 隐藏,选中效果取消
注意:这个交互当前的实现比较简单,没有考虑拖拽 mask、异常操做等,其最完整的形式咱们会在后文中给出,证实交互语法的完备性。
触发和反馈
从上面列举的框选过滤的示例中,咱们能够看到一个交互过程当中各个环节都须要触发对象、触发事件和反馈,咱们简单的将一个环节分为:
- 触发:触发的对象和触发事件
- 反馈:交互的影响是是什么
咱们能够看到交互的目的、概念模型贯穿整个过程,用户须要理解哪些对象能够进行交互,一个交互有哪些环节,每一个环节是否必要,是否清晰,交互的最终结果是否符合用户的目标。
交互语法的实现
理论距离实现由很大的距离,在可视化的过程当中实现交互语法,须要综合考虑交互目的、交互过程、触发和反馈。咱们依然以 G2 为示例,讲解交互语法从设计到实现的整个过程。经过上面的讨论咱们能够抽象出交互的概念模型:
- 一个图表支持多个交互
- 一个交互有多个交互环节
- 每一个交互环节能够有多个触发和反馈
- 一个触发有触发的对象和触发的事件
- 一个反馈也有反馈的对象和行为
结构图
经过上面的概念模型咱们能够得出交互的结构图: <a name="hZdIm"></a>
交互和交互环节
一个交互有多个交互环节,交互环节之间有顺序关系和并行关系:
- 只有一个交互开始(start) 才能结束(end)
- 只有一个交互开始才能持续(processing)
- 只有一个交互结束 (end) 才能回滚 (rollback)
<br>咱们对交互的过程进行命名:
- showEnable: 表示交互能够执行
- start: 交互触发
- processing: 交互持续
- end: 交互结束
- rollback:交互回滚
交互触发 Trigger
一个交互环节能够有多个触发和反馈,每一个触发分为:
- 触发对象
- 触发事件
触发对象和元素命名系统
咱们对 G2 全部能够触发交互的元素,都进行梳理而且命名:
- 图表元素:chart(图表)、view(视图)、geometry(几何标记表明图表类型)、element(数据对应的图形)、component(组件)
- 图表元素的包含部分和别名:
- plot(绘图区域)
- element 的在不一样 geometry 下的名称:point, interval, area 等
- 组件的内部图形:axis-label(坐标轴文本)、 legend-item(图例项)、annotation-line(辅助线) 等
触发事件
能够触发的事件有浏览器支持的全部事件:
- click, mousedown, mouseup, mouseenter, mouseleave 等事件
- drag, dragstart, dragend, dragenter, dragover, dragleave 等拖拽事件
- touch 等移动端事件
- 组件和图表的自定事件,如:图例的 valuechange ,chart 的 afterrender 等事件
触发对象和触发的事件能够组合使用,例如 'axis-lable:mouseenter'
交互反馈
每一个交互都须要反馈,以表示交互正在进行,同时最终的反馈也就是用户操做的目的。咱们将交互的反馈命名为 Action,反馈也分为:
- 反馈的对象
- 反馈的行为
反馈的对象
因为反馈要表示交互的能够进行、已经进行、持续和结束,咱们总结全部的交互反馈的对象,这些交互对象能够是:
- 数据源
- 鼠标形状
- 图表的图形
- 图表的组件
- 图表的容器 Chart 和 View
- 辅助的图形元素,遮罩层 Mask、图形的委托对象(拖拽)、操做按钮
反馈的行为
反馈的行为同反馈对象相关,就是每一个反馈对象能够支持的响应,例如:
- 鼠标能够支持:十字(crosshair)、指针(pointer)、默认(default)以及文本(text)等各类形状,每一个形状的转换就是一个反馈的行为。
- 数据源操做: 过滤(filter),增删改(add, update, remove) 等
- 图表容器的变化:画布大小变化、视图的位置变化
- 图表组件的变化: 高亮(highlight)、选中(selected)、激活(active)、 勾选(checked) 等状态变化;组件位置的变化等
反馈对象和行为的组合
反馈对象和行为也能够在交互,进行组合,可是因为一个反馈的对象能够有很是多的行为,而共同的行为有须要多个方法来实现一个反馈行为能够有多个方法,例如图形的高亮(highlight)包括:
- 高亮(highlight) ,设置元素高亮
- 恢复(reset) , 取消元素的高亮
- 清除(clear), 取消全部元素的高亮
咱们将上面的 Action 命名为 element-highlight
,一个反馈使用反馈行为和方法进行组合 'element-active:highlight', 'element-active:reset',<br>
同触发的组合
由此咱们就能够经过触发和反馈搭配出一个交互的多个环节:<br>环节一:
trigger: 'element:mouseenter' action: 'element-highlight:highlight'
环节二:
trigger: 'element:mouseleave' action: 'element-highlight:reset'
进行组合,就完成了图形 highlight 的交互:
{ start: { trigger: 'element:mouseenter' action: 'element-highlight:highlight' }, end: { trigger: 'element:mouseleave' action: 'element-highlight:reset' } }
组装交互
经过上面的简单的示例咱们能够看到交互能够经过定义交互环节,并在交互环节中配置触发和反馈来实现,回到咱们一开始的框选高亮为示例,来看一下如何组合出这个交互:
- 示能:
- 鼠标进入绘图区域时,变成十字
- 鼠标离开绘图区域时,变成默认形状
showEnable: [ { trigger: 'plot:mouseenter', action: 'cursor:crosshair' }, { trigger: 'plot:mouseleave', action: 'cursor:default' }, ]
- 开始
- 鼠标在绘图区域按下时,开始显示矩形的遮罩层(rect-mask),并显示
start: [{ trigger: 'plot:mousedown', action: ['rect-mask:start', 'rect-mask:show'], }]
- 持续
- 鼠标持续移动,矩形的遮罩(rect-mask) 形状跟随鼠标变化
- 随着矩形遮罩(rect-mask)被遮罩的图形高亮
processing: [ {trigger: 'plot:mousemove', action:'rect-mask:resize'}, {trigger: 'rect-mask:change', action: 'element-highlight:highlight'} ]
- 结束
- 鼠标抬起时,矩形的遮罩(rect-mask) 结束变化
end: [ {trigger: 'plot:mouseup', action: 'rect-mask:end'} ]
- 回滚
- 双击画布,矩形的遮罩(rect-mask) 消失,高亮效果消失
rollback: [ {trigger: 'dblclick', action: ['rect-mask:hide', 'element-highlight:clear']} ]
更加完善
这个交互就完成了,可是咱们在操做中发现,框选后还要可以拖拽遮罩(rect-mask),拖拽到画布外面时须要异常处理等,咱们能够继续完善各个环节,最终效果以下:<br><br>最终的交互语法也就完成了:
{ showEnable: [ { trigger: 'plot:mouseenter', action: 'cursor:crosshair' }, { trigger: 'mask:mouseenter', action: 'cursor:move' }, { trigger: 'plot:mouseleave', action: 'cursor:default' }, { trigger: 'mask:mouseleave', action: 'cursor:crosshair' }, ], start: [ { trigger: 'plot:mousedown', isEnable(context) { // 不要点击在 mask 上从新开始 return !context.isInShape('mask'); }, action: ['rect-mask:start', 'rect-mask:show'], }, { trigger: 'mask:dragstart', action: ['rect-mask:moveStart'] } ], processing: [ { trigger: 'plot:mousemove', action: ['rect-mask:resize'], }, { trigger: 'mask:drag',action: ['rect-mask:move'] }, { trigger: 'mask:change', action: ['element-range-highlight:highlight'] } ], end: [ { trigger: 'plot:mouseup', action: ['rect-mask:end'] }, { trigger: 'mask:dragend', action: ['rect-mask:moveEnd']}, { trigger: 'document:mouseup', isEnable(context) { return !context.isInPlot(); }, action: ['element-range-highlight:clear', 'rect-mask:end', 'rect-mask:hide'], }, ], rollback: [{ trigger: 'dblclick', action: ['element-range-highlight:clear', 'rect-mask:hide'] }], }
其余示例
咱们已经将全部 G2的交互所有经过交互语法进行了组装,开发交互的效率和质量获得极大的提高,下面是一些示例: <br>
<br>
总结
目前 G2 4.0 已经完成上面全部环节的设计和开发,并已经实现 3.x 默认的全部交互,你也能够自由组合出更多的交互,咱们还将在拖拽组件/视图重布局、拖拽图形重计算、拖拽合并以及图例排序等方面进行进行交互语法的组装。G2 4.0将于 2020 年 2月 27 日正式发布,敬请期待!
G2 官网: https://g2.antv.vision/zh/
github: https://github.com/antvis/G2