G2 从 4.0 开始,将所有的交互行为使用全新的交互语法实现,并且不再默认内置,需要用户显式调用chart.interaction()
接口。为了帮助大家更好地理解、使用交互语法,我们将会推出交互语法专题。
本文介绍的是最常见的交互:框选,包括框选的形状、框选过程中的图形变化以及框选后的各种操作。
框选中,高亮图形 | 框选后,拖拽 mask |
框选后,过滤数据 |
---|---|---|
框选后,过滤图形 | 框选的形状选择 | 多视图的框选联动 |
G2 的交互语法,是将交互拆解成多个环节,每个环节由触发和反馈组成。只要你能将交互用自然语言的方式描述出来,就可以使用 G2 的交互语法进行组合搭建出交互行为。在这里我们再一起温习下 G2 交互语法中对于交互环节的定义,更详细的内容可以阅读可视化交互语法。
G2 将每一个交互环节拆解成以下步骤:
下面我们就开始框选高亮图形的交互语法组装吧,为了帮助大家理解,每个交互行为我们都会以自然语言 + 交互语法的形式向大家阐述。我们以柱状图的高亮为例,实现交互的过程中我们会使用 G2 内置的 Action,Action 的定义和列表参考 G2 配置交互。
registerInteraction('element-range-highlight', { showEnable: [ { trigger: 'plot:mouseenter', action: 'cursor:crosshair' }, { trigger: 'plot:mouseleave', action: 'cursor:default' }, ], start: [ { trigger: 'plot:mousedown', action: ['rect-mask:start', 'rect-mask:show'], } ], processing: [ { trigger: 'plot:mousemove', action: ['rect-mask:resize', 'element-range-highlight:highlight'], }, ], end: [ { trigger: 'plot:mouseup', action: ['rect-mask:end'] }, ], rollback: [ { trigger: 'dblclick', action: ['element-range-highlight:clear', 'rect-mask:hide'] } ], }); 复制代码
我们使用了三个 Action 来实现这个区域高亮功能:
cursor
控制鼠标样式的 Action,这个 Action 的方法支持所有的鼠标样式,例如 'pointer'、'crosshair'、'move' 等。rect-mask
矩形的遮罩层,这个 Action 支持的方法有:
element-range-highlight
图表元素的区域高亮,支持的方法有:
**
交互语法解释:
showEnable
意味着交互是否可以进行; start
表示交互开始进行; processing
表示交互持续进行; end
表示交互结束; rollback
表示交互回滚。这些过程不完全是顺序的,例如:框选结束后,不需要回滚就可以继续开始新的框选; showEnable
在各个环节中都生效。end
环节中的 action 增加 'rect-mask:hide' 这时候框选结束后遮罩层消失,但是框选的高亮效果还存在。框选高亮后我们可以开始新的框选,但是如果能够拖拽遮罩层 (mask)使得被遮罩的图形高亮,体验更好。
这个交互的步骤如下:
注意这个交互同上面交互的差异,新增了 2、5 和 6 三个步骤,遮罩层的变化 3 需要增加触发条件。
registerInteraction('element-range-highlight', { 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']}, ], rollback: [{ trigger: 'dblclick', action: ['element-range-highlight:clear', 'rect-mask:hide'] }], }); 复制代码
我们根据这个交互同前一个交互的差别,来逐条增加新的步骤(触发和反馈):
showEnable
上增加鼠标移入 mask 和移出 mask 的效果。start
环节中触发遮罩层变化的步骤中增加是否在遮罩层上触发的判定。start
环节中增加拖拽遮罩层,在 processing
中增加遮罩层移动、图形根据遮罩层变化而高亮的步骤。通过这个示例,我们可以看到如何来扩展一个交互。框选本身不是交互的目的,框选后的操作才是框选交互的目的,框选后可以进行数据过滤、详情展示、显示隐藏等,下面我们通过几个交互来进行说明。
框选后对数据进行过滤时常见的操作,为了让用户意识到过滤已经发生,并且显示的告诉用户如何恢复,我们在框选发生过滤够显示了一个 reset 按钮。
registerInteraction('brush', { showEnable: [ { trigger: 'plot:mouseenter', action: 'cursor:crosshair' }, { trigger: 'plot:mouseleave', action: 'cursor:default' }, { trigger: 'reset-button:mouseenter', action: 'cursor:pointer' }, { trigger: 'reset-button:mouseleave', action: 'cursor:crosshair' }, ], start: [ { trigger: 'plot:mousedown', action: ['brush:start', 'rect-mask:start', 'rect-mask:show'], }, ], processing: [ { trigger: 'plot:mousemove', action: ['rect-mask:resize'], }, ], end: [ { trigger: 'plot:mouseup', action: ['brush:filter', 'brush:end', 'rect-mask:end', 'rect-mask:hide', 'reset-button:show'], }, ], rollback: [ { trigger: 'reset-button:click', action: ['brush:reset', 'reset-button:hide', 'cursor:crosshair'] }], }); 复制代码
我们使用了四个 Action 来实现这个区域高亮功能, cursor 和 rect-mask 已经介绍过,这里介绍其他两个:
brush
通过指定范围来过滤数据,有下面几个方法:
reset-button
恢复按钮,仅有显示和隐藏两个方法:
**
几点说明:
brush-x
仅仅过滤 x 轴范围内的数据;brush-y
仅仅过滤 y 轴范围内的数据。对应 rect-mask 也有两个 Action: x-rect-mask,y-rect-mask。
框选后不进行数据过滤,而仅仅控制图形的显示隐藏也是常见的,交互,我们来看一下这个交互的实现:
registerInteraction('brush-visible', { showEnable: [ { trigger: 'plot:mouseenter', action: 'cursor:crosshair' }, { trigger: 'plot:mouseleave', action: 'cursor:default' }, ], start: [ { trigger: 'plot:mousedown', action: ['rect-mask:start', 'rect-mask:show', 'element-range-highlight:start'], }, ], processing: [ { trigger: 'plot:mousemove', action: ['rect-mask:resize','element-range-highlight:highlight'], }, {trigger: 'mask:end',action: ['element-filter:filter']} ], end: [ { trigger: 'plot:mouseup', action: ['rect-mask:end', 'rect-mask:hide', 'element-range-highlight:end', 'element-range-highlight:clear'], }, ], rollback: [ { trigger: 'dblclick', action: ['element-filter:clear'] } ] }); 复制代码
这个交互中我们使用到了,其中 cursor
、 rect-mask
和 element-range-highlight
三个 Action 前面已经介绍到,这里对新的 Action element-filter
进行说明:
element-filter
[8]:过滤图表元素,有两个方法:
从上面的几个交互我们可以看到,多个交互之间会共享大量的 Action,这就解决了交互代码复用的问题,为提升开发交互的效率和提升质量提供了保障。
这个交互的步骤同前面的几个交互类似,最大的差别在于:鼠标在画布上拖拽时,根据鼠标移动的轨迹改变遮罩层的形状。
registerInteraction('element-range-highlight', { 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: ['path-mask:start', 'path-mask:show'], }, { trigger: 'mask:dragstart', action: ['path-mask:moveStart'] } ], processing: [ { trigger: 'plot:mousemove', action: ['path-mask:resize'], }, { trigger: 'mask:drag',action: ['path-mask:move'] }, { trigger: 'mask:change', action: ['element-range-highlight:highlight'] }, {trigger: 'mask:end',action: ['element-filter:filter']} ], end: [ { trigger: 'plot:mouseup', action: ['path-mask:end'] }, { trigger: 'mask:dragend', action: ['path-mask:moveEnd']}, ], rollback: [ { trigger: 'dblclick', action: ['element-range-highlight:clear', 'path-mask:hide', 'element-filter:clear'] }], }); 复制代码
这个交互同上面 “框选后的操作-拖拽” 完全一致,除了显示遮罩层的 Action 从 rect-mask
替换成 path-mask
之外,这个 Action 同 rect-mask
的方法完全一致:
path-mask
矩形的遮罩层,这个 Action 支持的方法有:
除了 rect-mask
和 path-mask
之外 G2 还内置了其他两种 circle-mask
和 smooth-path-mask
,除了形状不同外提供的方法完全相同。
registerInteraction('highlight-view', { 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) { 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', isEnable(context) { return context.isInPlot(); }, action: 'rect-mask:move'}, { trigger: 'mask:change', action: ['element-sibling-highlight:highlight', 'element-range-highlight:highlight'] } ], end: [ { trigger: 'plot:mouseup', action: 'rect-mask:end' }, { trigger: 'mask:dragend', action: 'rect-mask:moveEnd' }, { trigger: 'document:mousedown', isEnable(context) { return !context.isInPlot(); }, action: ['element-sibling-highlight:clear', 'element-range-highlight:clear', 'rect-mask:end', 'rect-mask:hide'], once: true, }, { trigger: 'document:mouseup', isEnable(context) { return !context.isInPlot(); }, action: ['rect-mask:end'], once: true, } ], rollback: [ { trigger: 'dblclick', action: ['rect-mask:hide', 'element-sibling-highlight:clear', 'element-range-highlight:clear']} ] }); 复制代码
这个交互同前面提到 “框选后的操作-拖拽” 几乎一模一样,除了在调用 Action element-range-highlight
的同时也调用了 element-sibling-highlight
的方法,其含义是“高亮图形” 的同时 “高亮所有同级 views 的图形”。
element-sibling-highlight
[10]:高亮当前 view 同一级的 views 的对应图形,这个 Action 的方法有:
**
更多的解释:
细心的读者可能关注到这个交互中出现了几个前面交互中没有出现的步骤(触发和反馈),这些都是一些异常处理的步骤,这关系到一个交互的质量:
document:mousedown
绘图区域之外按下起鼠标(面上其他位置 ),结束高亮并且隐藏遮罩层。document:mouseup
绘图区域之外抬起鼠标(面上其他位置 ),则结束 rect-mask 的变化。mask:drag
时添加约束条件,如果不再当前 View 的绘图区域,则不再移动。除了多个 View 之间联动高亮,还可以进行联动过滤、图形隐藏、tooltip 联动等操作,我们会在后面的章节中给大家介绍。
框选高亮作为图表中经常使用的交互,在不同的场景下框选的过程和结果都有可能变化,传统的固定死的交互方式并不能满足用户的需求。当我们有了交互语法,只要你能列出交互的步骤,就可以将这些步骤自然地转换成交互语法,高效而且高质,快去尝试一下吧!
G2 官网: g2.antv.vision/zh/
github: github.com/antvis/G2