可视化的交互语法

原创
02/20 18:01
阅读数 397

> 本文作者:AntV 架构师-萧庆

简介

Antv 过去 5 年中,在很多可视化领域进行了探索,在统计图表、可视分析、关系图、地理可视化等可视化场景中都面临如何把数据转换成图形(可视化编码)的问题,数据一旦以图形的方式呈现给用户,用户则需要在上面进行交互,查看数据细节从不同的层面对数据进行探查,以一句可视化领域常说的话来总结:“Overview first, zoom and filter, then details-on-demand"。 image.png > 《Visuallization Analysis&Design》是这个领域的一本非常经典的书籍,感兴趣的不能错过。

G2(统计图表) 、F2(移动端图表)、L7(地理可视化)和 G6(关系可视化) 都在 Overview first 上有了很大的收获,通过可视化编码我们已经探索了所有常见图表如何从数据转换成图形。从而在 G2、F2 上形成图形语法,在 L7 上使用地理符号学,在 G6上也支持数据映射到节点和边的状态,但是接下来的交互我们还没有形成统一的理论依据,各自为政。今天我们在 G2 4.0 上根据过去的经验和教训总结出一套交互语法,初步验证了完备性、易用性和实现交互的效率,我们将会推广到其他产品上。

交互回顾

以 G2 为例,我们在交互方面的探索主要有三个阶段:

  • 内置交互:可以允许的交互都写在代码内部,同渲染、数据更新流程耦合
  • 可注册交互:每个交互一个名称,可以增加配置项用以控制触发和反馈
  • 交互语法:将交互分为多个阶段,每个阶段分为触发和反馈,通过实现常见的反馈,来搭配触发和反馈组合出一套新的交互。

交互内置

图表的交互内置是一种直觉的实现方式,在通用图表的开发过程中,我们可以枚举每种图表支持的交互:

  • 所有图表都支持 tooltip
  • 图例能够进行数据过滤
  • 散点图可以使用矩形框框选,折线图可以框选 x 轴
  • 饼图点击可以沿着圆心方向向外移动

但是一旦图表库同产品进行结合事情就没那么简单,很多交互同图表相关,但是又与产品的设计相关:框选、过滤、排序和标注等交互需要在不同的产品中有不同的触发形式,而交互一旦内置,用户很难改变交互的触发方式和结果。

一旦用户面临交互的改造,我们的答疑成本会激增,作为图表的开发者也很难改变交互的形态只能在上面增加配置项,打上一个个的补丁。

可注册交互

出于交互可以扩展,用户可以自由定制交互的目的,我们在 G2 v3.4、F2 v3.3、G6 v2.1 版本上增加可以注册的交互,交互不再同渲染和图表的声明周期进行耦合,依然以 G2 为例:

股票指数走势探索 brush结合dataset
散点图缩放交互 brush过滤图形

其核心思想是:

  • 开放足够多的事件
  • 使用名称来代表一个交互,每种交互支持一定的配置项,使用时仅需一句代码
chart.interaction('brush');

这种方案对于扩展性和用户直接使用已经写好的交互有比较好的支持,但是来定制交互的用户需要深入理解各个产品的内部细节,例如:框选了画布,有哪些图形会被框选,图形对应的数据有哪些,如何进行过滤。

最终的结果是: 这套注册交互的机制,主要还是图表库(可视化工具)的开发者在使用,很多细节也实现的不够好 

更进一步

前面两种交互的实现方式有各种各样的问题,因此我们在寻找一种易于使用、易于扩展、易于理解的交互模式,由 G2 的图形语法我们想到了能否创造一套完备的交互语法。

交互语法

图形语法的提示

如何在不影响现有图表声明周期(渲染、更新)的前提下,用户可以自由搭配交互,这看似是个无解的问题,我们回到 G2 的起点 《The Grammar of Graphics》 ,Leland Wilkinson 将数据转换为图形的过程拆解为:

  • 度量:数据各个字段的特征,如何将数据映射到 0-1 空间
  • 视觉通道:数据如何用可视化的形式来展示
  • 统计函数:汇总统计、回归、密度计算、链接等数据计算
  • 坐标系:0-1 的数据如何映射到位置上
  • 几何标记:抽象所有图表类型,总结出7种抽象的几何类型

image.png image.png<br>image.pngimage.png

交互语法的理论依据

回到可视化的交互方面,并没有一套成熟的理论,能够提供出足够的抽象,将现有的交互总结到一套框架中。我们在 唐纳德·A·诺曼 的 《设计心理学》中找到依据,他将交互过程划分为不同的阶段: image.png<br>同时设计心理学中有 5 个核心的概念:

  • 示能:示能指的是物品与人之间的关系,物品的特性与决定物品预设用途的主体的能力之间的关系。
  • 意符:意符是一种提示,告诉用户可以采取什么行为,以及应该怎么操作。
  • 映射:映射表示两组事物要素之间的关系,通常用在控制与显示的设计上。
  • 反馈:交互必须有反馈,告诉用户交互正在执行,同时反馈同样是交互的结果。
  • 概念模型:概念模型是高度简化的说明,告诉用户产品如何工作。让用户能够充分的理解交互的整个过程。

可视化的交互语法

综合前面图形语法和设计心理学中的理论依据,我们对可视化的交互进行分析,总结出交互的目的,同时将可视化交互过程划分为多个交互环节,每个过程分别有触发和反馈。 <a name="KSxPk"></a>

交互目的

交互的目的也就是用户使用交互的意图,交互意图是否能够满足,是交互设计的根本出发点。在可视化的交互中,用户需要使用交互来完成:

  • 数据定义,确定哪些数据展示,对数据进行操作、加工、探索<br>
  • 视图操作,在不同的视图上操作展示的数据,可以在视图上对数据进行选中、导航、调整数据的展示方式等<br>
  • 交互记录,记录交互的轨迹,可以进行交互过程的展示

交互的目的决定: 用户从哪里开始交互,最终的交互结果是什么 <br>对交互目的的详细说明,参看 交互概述

交互过程

我们将交互聚焦于可视化的场景时,整理了常见的交互后,发现这些交互都有一致的过程,我们可以将一个交互分解成多个交互环节:

  • 示能:表示交互可以进行
  • 开始:交互开始
  • 持续:交互持续
  • 结束:交互结束
  • 回滚:取消交互,恢复到原始状态

大多数的交互仅使用上面的部分过程,但是也有些交互需要所有的过程,我们以一个框选高亮为示例: highlight.gif

  1. 示能:
  • 触发对象:画布
  • 触发事件:移动进画布绘图区域、移出画布绘图区域
  • 反馈:鼠标形状变成十字、离开时鼠标形状恢复
  1. 开始:
  • 触发对象:画布
  • 触发事件:按下鼠标,并滑动鼠标
  • 反馈:出现 mask
  1. 持续 1
  • 触发对象:画布
  • 触发事件:持续滑动鼠标
  • 反馈:mask 随着鼠标变化
  1. 持续 2
  • 触发对象:mask
  • 触发事件:mask 的大小变化
  • 反馈:被 mask 遮挡的图形高亮
  1. 结束:
  • 触发对象:画布
  • 触发事件:鼠标抬起
  • 反馈:mask 依然显示,遮挡的图形继续高亮
  1. 回滚:
  • 触发对象:画布
  • 触发事件:鼠标双击
  • 反馈:mask 隐藏,选中效果取消

注意:这个交互当前的实现比较简单,没有考虑拖拽 mask、异常操作等,其最完整的形式我们会在后文中给出,证明交互语法的完备性。

触发和反馈

从上面列举的框选过滤的示例中,我们可以看到一个交互过程中各个环节都需要触发对象、触发事件和反馈,我们简单的将一个环节分为:

  • 触发:触发的对象和触发事件
  • 反馈:交互的影响是是什么

我们可以看到交互的目的、概念模型贯穿整个过程,用户需要理解哪些对象可以进行交互,一个交互有哪些环节,每个环节是否必要,是否清晰,交互的最终结果是否符合用户的目标。

交互语法的实现

理论距离实现由很大的距离,在可视化的过程中实现交互语法,需要综合考虑交互目的、交互过程、触发和反馈。我们依然以 G2 为示例,讲解交互语法从设计到实现的整个过程。通过上面的讨论我们可以抽象出交互的概念模型:

  • 一个图表支持多个交互
  • 一个交互有多个交互环节
  • 每个交互环节可以有多个触发和反馈
  • 一个触发有触发的对象和触发的事件
  • 一个反馈也有反馈的对象和行为

结构图

通过上面的概念模型我们可以得出交互的结构图: <a name="hZdIm"></a>

交互和交互环节

一个交互有多个交互环节,交互环节之间有顺序关系和并行关系:

  • 只有一个交互开始(start) 才能结束(end)
  • 只有一个交互开始才能持续(processing)
  • 只有一个交互结束 (end) 才能回滚 (rollback)

image.png<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、图形的委托对象(拖拽)、操作按钮

brush-3.gif

反馈的行为

反馈的行为同反馈对象相关,就是每个反馈对象可以支持的响应,例如:

  • 鼠标可以支持:十字(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>highlight1.gif

同触发的组合

由此我们就可以通过触发和反馈搭配出一个交互的多个环节:<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'
  }
}

组装交互

通过上面的简单的示例我们可以看到交互可以通过定义交互环节,并在交互环节中配置触发和反馈来实现,回到我们一开始的框选高亮为示例,来看一下如何组合出这个交互:

  1. 示能:
  2. 鼠标进入绘图区域时,变成十字
  3. 鼠标离开绘图区域时,变成默认形状
showEnable: [
	{ trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
  { trigger: 'plot:mouseleave', action: 'cursor:default' },
]
  1. 开始
  2. 鼠标在绘图区域按下时,开始显示矩形的遮罩层(rect-mask),并显示
start: [{
  trigger: 'plot:mousedown',
  action: ['rect-mask:start', 'rect-mask:show'],
}]
  1. 持续
  2. 鼠标持续移动,矩形的遮罩(rect-mask) 形状跟随鼠标变化
  3. 随着矩形遮罩(rect-mask)被遮罩的图形高亮
processing: [
  {trigger: 'plot:mousemove', action:'rect-mask:resize'},
  {trigger: 'rect-mask:change', action: 'element-highlight:highlight'}
]
  1. 结束
  2. 鼠标抬起时,矩形的遮罩(rect-mask) 结束变化
end: [
  {trigger: 'plot:mouseup', action: 'rect-mask:end'}
]
  1. 回滚
  2. 双击画布,矩形的遮罩(rect-mask) 消失,高亮效果消失
rollback: [
  {trigger: 'dblclick', action: ['rect-mask:hide', 'element-highlight:clear']}
]

highlight.gif

更加完善

这个交互就完成了,但是我们在操作中发现,框选后还要能够拖拽遮罩(rect-mask),拖拽到画布外面时需要异常处理等,我们可以继续完善各个环节,最终效果如下:<br>drag-mask1.gif<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的交互全部通过交互语法进行了组装,开发交互的效率和质量得到极大的提升,下面是一些示例: selected.giflegend-error1.gif<br>tooltip-views1.gifview-sync.gifsync-record.gifsync-visible.gif<br>sync-filter3.gifpolygon-brush5.gif

总结

目前 G2 4.0 已经完成上面所有环节的设计和开发,并已经实现 3.x 默认的所有交互,你也可以自由组合出更多的交互,我们还将在拖拽组件/视图重布局、拖拽图形重计算、拖拽合并以及图例排序等方面进行进行交互语法的组装。G2 4.0将于 2020 年 2月 27 日正式发布,敬请期待!

G2 官网: https://g2.antv.vision/zh/

github: https://github.com/antvis/G2

展开阅读全文
打赏
1
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
1
分享
返回顶部
顶部