G2 从 4.0 开始,将所有的交互行为使用全新的交互语法实现,并且不再默认内置,需要用户显式调用
chart.interaction() 接口。为了帮助大家更好地理解、使用交互语法,我们将会推出交互语法专题。

本文介绍的是最常见的交互过滤,包括图形的过滤和数据过滤,这两种过滤适合的场景不同,G2 的交互语法可以允许用户进行随意的搭配。本章主要介绍组件导致的过滤、框选过滤和多 View 的过滤联动。

数据过滤

图表的数据发生过滤时,不但图表上的图形会发生变化,坐标轴也会变化。从触发数据过滤的场景来看,G2 的数据过滤主要有三种情况:组件导致的过滤、框选导致的过滤和多 View 之间的过滤。

组件导致的过滤

filter.gif

语言描述

  • 图例发生改变时,对图表的数据进行过滤

G2 的交互语法

  1. registerInteraction('continuous-filter', {
  2. start: [{ trigger: 'legend:valuechanged', action: 'data-filter:filter' }],
  3. });

交互语法说明

可以看到这个交互非常简单,仅使用了 data-filter 一个 Action,这个 Action 仅有一个方法:

  • filter 过滤

框选过滤

b2.gif

语言描述

  1. 鼠标进入绘图区域时变成十字,离开绘图区域时恢复正常
  2. 鼠标进入按钮变成手型,离开恢复按钮变成十字
  3. 鼠标按下开始出现遮罩层,拖拽时遮罩层大小随着鼠标变化
  4. 释放鼠标,遮罩层消失,对图表数据进行过滤,同时出现恢复按钮
  5. 点击恢复按钮,数据过滤取消

G2 交互语法

  1. registerInteraction('brush', {
  2. showEnable: [
  3. { trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
  4. { trigger: 'plot:mouseleave', action: 'cursor:default' },
  5. { trigger: 'reset-button:mouseenter', action: 'cursor:pointer' },
  6. { trigger: 'reset-button:mouseleave', action: 'cursor:crosshair' },
  7. ],
  8. start: [
  9. {
  10. trigger: 'plot:mousedown',
  11. action: ['brush:start', 'rect-mask:start', 'rect-mask:show'],
  12. },
  13. ],
  14. processing: [
  15. {
  16. trigger: 'plot:mousemove',
  17. action: ['rect-mask:resize'],
  18. },
  19. ],
  20. end: [
  21. {
  22. trigger: 'plot:mouseup',
  23. action: ['brush:filter', 'brush:end',
  24. 'rect-mask:end', 'rect-mask:hide', 'reset-button:show'],
  25. },
  26. ],
  27. rollback: [{
  28. trigger: 'reset-button:click',
  29. action: ['brush:reset', 'reset-button:hide', 'cursor:crosshair']
  30. }],
  31. });

交互语法说明

这个交互用到了 4 个 Action:

  • cursor: 表示鼠标形状的 Action,所有的鼠标形状都是一个个的方法
  • rect-mask: 矩形遮罩层,有 5 个方法
    • show 显示
    • hide 隐藏
    • start 开始遮罩层
    • resize 遮罩层变化
    • end 结束遮罩层
  • brush:框选过滤,主要有 4 个方法:
    • start 设置开始框选的位置
    • end 设置结束框的位置
    • filter 过滤
    • reset 取消过滤

过滤联动

brush21.gif

语言描述

  1. 鼠标移入绘图区域鼠标变成十字,鼠标移出绘图区域鼠标恢复正常
  2. 鼠标移入遮罩层鼠标变成移动,鼠标移出遮罩层鼠标恢复十字
  3. 鼠标不在遮罩层上按下遮罩层生成,拖拽鼠标遮罩层大小变化,同时其他 View 发生数据过滤
  4. 鼠标拖动遮罩层,遮罩层位置变化,同时其他 View 发生数据过滤
  5. 双击画布,取消过滤

G2 交互语法

  1. registerInteraction('other-filter', {
  2. showEnable: [
  3. { trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
  4. { trigger: 'mask:mouseenter', action: 'cursor:move' },
  5. { trigger: 'plot:mouseleave', action: 'cursor:default' },
  6. { trigger: 'mask:mouseleave', action: 'cursor:crosshair' },
  7. ],
  8. start: [
  9. { trigger: 'plot:mousedown',isEnable(context) {
  10. return !context.isInShape('mask');
  11. }, action: ['x-rect-mask:start', 'x-rect-mask:show'] },
  12. {trigger: 'mask:dragstart', action: 'x-rect-mask:moveStart'}
  13. ],
  14. processing: [
  15. { trigger: 'plot:mousemove', action: 'x-rect-mask:resize' },
  16. { trigger: 'mask:drag', action: 'x-rect-mask:move'},
  17. { trigger: 'mask:change', action: 'sibling-x-filter:filter' }
  18. ],
  19. end: [
  20. { trigger: 'plot:mouseup', action: 'x-rect-mask:end' },
  21. { trigger: 'mask:dragend', action: 'x-rect-mask:moveEnd' }
  22. ],
  23. rollback: [
  24. {
  25. trigger: 'dblclick',
  26. action: ['x-rect-mask:hide', 'sibling-x-filter:reset']
  27. }
  28. ]
  29. });

交互语法说明

这个交互虽然表现同上面的框选过滤差异比较大,但是整体思想非常像,用到的 Action 也存在关联

  • sibling-x-filter 这个 Action 是从 brush 扩展出来的,仅仅进行 x 轴方向的过滤,这个 Action 的作用并不是应用在当前 view 上,而是应用在所有同一级的其他 view 上。
  • x-rect-mask 是从 rect-mask 继承出来的,所以使用方法同 rect-mask 完全一致

图形过滤

图形过滤仅仅控制图形的显示隐藏,而不导致坐标轴的变化,是一种比较高效的过滤方式。

组件导致的过滤

filter6.gif

语言描述

  • 拖动图例,图表上的图形跟随图例的范围显示、隐藏

G2 交互语法

  1. registerInteraction('continuous-visible-filter', {
  2. start: [{ trigger: 'legend:valuechanged', action: 'element-filter:filter' }],
  3. });

交互语法说明

图例变化对图形进行过滤的交互同数据过滤的交互唯一的差别就是使用了 element-filter 而不是 data-filter,这个 Action 有两个方法:

  • filter 图形过滤
  • clear 清除图形过滤

框选过滤

filter7.gif

语言描述

  1. 鼠标移入绘图区域变成十字,鼠标离开绘图区域恢复原状
  2. 鼠标按下遮罩层开始出现,拖拽鼠标遮罩层大小变化,同时框选的图形高亮
  3. 抬起鼠标对框选选中的图形进行过滤
  4. 双击画布,清除图形的过滤

G2 交互语法

  1. registerInteraction('brush-visible', {
  2. showEnable: [
  3. { trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
  4. { trigger: 'plot:mouseleave', action: 'cursor:default' },
  5. ],
  6. start: [
  7. {
  8. trigger: 'plot:mousedown',
  9. action: ['rect-mask:start', 'rect-mask:show'],
  10. },
  11. ],
  12. processing: [
  13. {
  14. trigger: 'plot:mousemove',
  15. action: ['rect-mask:resize'],
  16. },
  17. {trigger: 'mask:change',action: ['element-range-highlight:highlight']}
  18. ],
  19. end: [
  20. {
  21. trigger: 'plot:mouseup',
  22. action: ['rect-mask:end', 'rect-mask:hide',
  23. 'element-filter:filter', 'element-range-highlight:clear'],
  24. },
  25. ],
  26. rollback: [
  27. {
  28. trigger: 'dblclick',
  29. action: ['element-filter:clear']
  30. }
  31. ]
  32. });

交互语法说明

这个交互的流程比较清晰,除了使用了前面交互使用到的几个 Action 之外,还用到了 element-range-highlight 范围内高亮图形的 Action,这个 Action 主要有两个方法:

  • highlight 图形高亮
  • clear 清除图形高亮

过滤联动

filter8.gif

语言描述

  1. 鼠标移动进入绘图区间鼠标显示成十字,移出绘图区间鼠标恢复正常。
  2. 鼠标移入遮罩层显示成移动形状,移出恢复成十字
  3. 在画布空白处按下鼠标,遮罩层开始,拖动时遮罩层改变大小,同时其他 View 的图形发生过滤
  4. 拖动遮罩层,遮罩层移动,同时其他 View 的图形发生过滤
  5. 双击隐藏遮罩层,图形过滤取消

G2 交互语法

  1. registerInteraction('other-visible', {
  2. showEnable: [
  3. { trigger: 'plot:mouseenter', action: 'cursor:crosshair' },
  4. { trigger: 'mask:mouseenter', action: 'cursor:move' },
  5. { trigger: 'plot:mouseleave', action: 'cursor:default' },
  6. { trigger: 'mask:mouseleave', action: 'cursor:crosshair' },
  7. ],
  8. start: [
  9. {
  10. trigger: 'plot:mousedown', isEnable(context) {
  11. return !context.isInShape('mask');
  12. }, action: ['rect-mask:start', 'rect-mask:show']
  13. },
  14. { trigger: 'mask:dragstart', action: 'rect-mask:moveStart' }
  15. ],
  16. processing: [
  17. { trigger: 'plot:mousemove', action: 'rect-mask:resize' },
  18. {
  19. trigger: 'mask:drag', isEnable(context) {
  20. return context.isInPlot();
  21. }, action: 'rect-mask:move'
  22. },
  23. { trigger: 'mask:change', action: 'element-sibling-filter:filter' }
  24. ],
  25. end: [
  26. { trigger: 'plot:mouseup', action: 'rect-mask:end' },
  27. { trigger: 'mask:dragend', action: 'rect-mask:moveEnd' }
  28. ],
  29. rollback: [
  30. { trigger: 'dblclick',
  31. action: ['rect-mask:hide', 'element-sibling-filter:reset'] }
  32. ]
  33. });

交互语法的说明

同上面的过滤联动的示例类似,这个在一个 View 上框选其他 View 上产生过滤的案例,仅有一个特别的 Action:element-sibling-filter ,这个 Action 会作用去其他 View 上,所有被遮罩层对应位置遮罩的图形都会发生过滤,有两个方法:

  • filter 过滤
  • reset 取消过滤

过滤交互的性能提升

无论图形过滤还是数据过滤都是开销比较大的操作,很容易导致页面卡顿,仔细分析原因大部分是由于触发过滤操作的频率过高,这时候对交互限流就可以显著提升过滤交互的性能。G2 的交互语法已经提供了限流功能,更详细的参考 《交互语法中的限流》

限流示例

你可以在上面的任意一个示例上增加 throttle 或者 debounce 的配置,我们在图例过滤时增加限流,为了将效果表现的明显我们将时间延迟增大到 500ms,正常情况下 100ms 即可:

throttle

filter3.gif

  1. registerInteraction('continuous-filter', {
  2. start: [
  3. {
  4. trigger: 'legend:valuechanged',
  5. action: 'data-filter:filter',
  6. throttle: { wait: 500, leading: true, trailing: false }
  7. }
  8. ],
  9. });

可以明显的看出触发过滤的频率在下降,单次动画的完成程度比较高。

debounce

filter4.gif

  1. registerInteraction('continuous-filter', {
  2. start: [
  3. {
  4. trigger: 'legend:valuechanged',
  5. action: 'data-filter:filter',
  6. debounce: { wait: 500, immediate: false }
  7. }
  8. ],
  9. });

只有在停止改变图例的滑动后,过滤才会触发,在数据量比较大的场景下这是一个不错的选择。

总结

本章介绍了几种常见的数据/图形过滤场景,是选择数据过滤还是图形过滤需要根据具体的场景具体选择,一般情况下图形过滤的性能比数据过滤更好,在图形非常多的情况下优先选择。但是由于数据过滤会引起坐标轴的变化,在一些折线图的场景下,优先选择数据过滤,因为可以将不在数据范围内的空间省略掉,更容易的看清楚数据。

参考资料

  1. 可视化交互语法
  2. G2 内置交互反馈
  3. data-filter