简介

Element 的交互可以分为:

  • 状态变换的交互,例如: active, selected
  • 显示隐藏
  • 拖拽、框选
  • 编辑
  • 快捷键

    Element Action

    基本的 Action

    基本的 Action 有:

  • ElementVisible

  • ElementActive
  • ElementSelected
  • ElementMove
  • ElementDelegation
  • ElementResize
  • ElementFilter
  • ElementCombine
  • ElementDelete

Action 的扩展

每个 Action 可以扩展出更多的 Action,适应不同类型的 Element

  1. class ElementAction extends Action {
  2. show() {}
  3. hide() {}
  4. remove() {}
  5. }
  6. class ElementResize extends Action {
  7. start() {}
  8. resize() {}
  9. end() {}
  10. reset() {}
  11. }
  12. // 扩展新的 Action
  13. class BubbleResize extends ElementResize {
  14. }
  15. class ElementSelected extends Action {
  16. selected() {}
  17. clear() {}
  18. }
  19. // 扩展饼图选中
  20. class PieSelected extends ElementSelected {
  21. selected() {}
  22. clear() {}
  23. }

状态变化

active

Element 的 active 有三种形态:

  • 单个 Element active
  • 多个 Element 一起 active
  • 部分高亮,部分变暗
  1. class ElementActiveAction extends Action {
  2. getActiveElements() {}
  3. active() {
  4. // context
  5. const activeElements = this.getActiveElements()
  6. activeElements.forEach(el => {
  7. el.setState('active', true);
  8. });
  9. }
  10. reset() {
  11. const activeElements = context.view.get('activeElements');
  12. activeElements.forEach(el => {
  13. el.setState('active', false);
  14. });
  15. }
  16. }
  17. class ElementHiglightAction extends ElementActiveAction {
  18. highlight() {
  19. const activeElements = this.getActiveElements()
  20. activeElements.forEach(el => {
  21. el.setState('active', true);
  22. });
  23. }
  24. reset() {}
  25. }
  26. G2.RegisterAction('A', ElementActiveAction);

hover 到 element ,单个 element active

  1. G2.RegisterInteraction('element-active', {
  2. start: [
  3. {trigger: 'element:mouseenter', action: 'ElementActive:active'}
  4. ],
  5. end: [
  6. {trigger: 'element:mouseleave', action: 'element-active:reset'}
  7. ]
  8. });

hover 到 axis label 上,对应的 element 高亮

  1. G2.RegisterInteraction('element-axis-active', {
  2. start: [
  3. {trigger: 'axis-label:mouseenter', action: 'element-active:active'}
  4. ],
  5. end: [
  6. {trigger: 'axis-label:mouseleave', action: 'element-active:reset'}
  7. ]
  8. });

hover 到 legend 上,对应的 element 高亮

  1. G2.RegisterInteraction('element-legend-active', {
  2. start: [
  3. {trigger: 'legend-item:mouseenter', action: 'element-active:active'}
  4. ],
  5. end: [
  6. {trigger: 'legend-item:mouseleave', action: 'element-active:reset'}
  7. ]
  8. });

hover 到 element 上 highlight

  1. G2.RegisterInteraction('element-higlight', {
  2. start: [
  3. {trigger: 'element:mouseenter', action: 'element-highlight:highlight'}
  4. ],
  5. end: [
  6. {trigger: 'element:mouseleave', action: 'element-highlight:reset'}
  7. ]
  8. });

hover 到 legend 上

  1. G2.RegisterInteraction('element-legend-higlight', {
  2. start: [
  3. {trigger: 'legend-item:mouseenter', action: 'element-higlight:active'}
  4. ],
  5. end: [
  6. {trigger: 'legend-item:mouseleave', action: 'element-higlight:reset'}
  7. ]
  8. });

鼠标附近 x 轴上的多个 elmement active

鼠标附近多个 elmement 高亮,需要定义一个新的 Action

  1. class IntervalActiveByX extends Action {
  2. active() {}
  3. reset() {}
  4. }
  5. // 可以随便命名
  6. class PointActiveByXY extends Action {
  7. active() {}
  8. reset() {}
  9. }
  10. G2.RegisterAction('interval-active-by-x', IntervalActiveByX);
  11. G2.RegisterAction('point-active-by-xy', IntervalActiveByX);
  1. G2.RegisterInteraction('element-active-by-x', {
  2. start: [
  3. {trigger: 'view:plotenter', action: 'interval-active-by-x:active'}
  4. ],
  5. processing: [
  6. {trigger: 'view:plotmove', action: 'interval-active-by-x:active'}
  7. ],
  8. end: [
  9. {trigger: 'view:plotenter', action: 'interval-active-by-x:reset'}
  10. ]
  11. });

x,y 最近的 Element 高亮

  1. G2.RegisterInteraction('element-active-by-x', {
  2. start: [
  3. {trigger: 'view:plotenter', action: 'point-active-by-xy:active'}
  4. ],
  5. processing: [
  6. {trigger: 'view:plotmove', action: 'point-active-by-xy:active'}
  7. ],
  8. end: [
  9. {trigger: 'view:plotenter', action: 'point-active-by-xy:reset'}
  10. ]
  11. });

interval 相同颜色的高亮

  1. class IntervalActiveByColor extends Action {
  2. active() {}
  3. reset() {}
  4. }
  1. G2.RegisterInteraction('element-active-by-x', {
  2. start: [
  3. {trigger: 'element:mouseenter', action: 'interval-active-by-color:active'}
  4. ],
  5. end: [
  6. {trigger: 'element:mouseleave', action: 'interval-active-by-color:reset'}
  7. ]
  8. });

selected

元素的选中包括:

  • 单选
  • 多选
  • 框选
  1. class ElementSinlgeSelectedAction extends Action {
  2. selected() {}
  3. cancel() {}
  4. }
  5. class ElementMultipleSelectedAction extends Action {
  6. selected() {}
  7. unselected() {}
  8. reset() {}
  9. }
  10. class ElementRangeSelectedAction extends Action {
  11. start() {}
  12. selected() {}
  13. reset() {}
  14. }
  15. class ElementMaskAction extends Action {
  16. start() {}
  17. resize() {}
  18. hide() {}
  19. }
  20. class ElementDelegationAction extends Action {
  21. show() {}
  22. move() {}
  23. hide() {}
  24. }
  25. G2.registerAction('element-single-selected', ElementSinlgeSelectedAction);
  26. G2.registerAction('element-multiple-selected', ElementMultipleSelectedAction);
  27. G2.registerAction('element-mask', ElementRangeSelectedAction);

点击单选 selected,可取消

  1. G2.RegisterInteraction('element-selected',{
  2. start: [
  3. {trigger: 'element:click', action: 'element-single-selected:selected'}
  4. ],
  5. end: [
  6. {trigger: 'element:click', action: 'element-single-selected:reset'}
  7. ]
  8. });

点击单选,点击空白取消选中

  1. G2.RegisterInteraction('element-selected',{
  2. start: [
  3. {trigger: 'element:click', action: 'element-single-selected:selected'}
  4. ],
  5. end: [
  6. {trigger: 'view:click', isEnable(context) {
  7. return !context.event.element; // 未点击到时取消
  8. }, action: 'element-single-selected:reset'}
  9. ]
  10. });

多选,再次点击取消

  1. G2.RegisterInteraction('element-multiple-selected', {
  2. start: [
  3. {trigger: 'element:click', action: 'element-multiple-selected:selected'}
  4. ],
  5. end: [
  6. {trigger: 'element:click', action: 'element-multiple-selected:unselected'}
  7. ]
  8. });

框选,无 mask,中间过程无选中

  1. G2.RegisterInteraction('element-multiple-selected', {
  2. start: [
  3. {trigger: 'view:mousedowm', action: 'element-range-selected:start'}
  4. ],
  5. end: [
  6. {trigger: 'view:mouseup', action: 'element-range-selected:selected'}
  7. ]
  8. });

框选,无 mask,中间过程可选中

  1. G2.RegisterInteraction('element-multiple-selected', {
  2. start: [
  3. {trigger: 'view:mousedowm', action: 'element-range-selected:start'}
  4. ],
  5. processing: [
  6. {trigger: 'view:mousemove', action: 'element-multiple-selected:selected'}
  7. ],
  8. end: [
  9. {trigger: 'view:mouseup', action: 'element-range-selected:selected'}
  10. ],
  11. rollback: [
  12. {trigger: 'view:click', action: 'element-range-selected:reset'}
  13. ]
  14. });

框选,有 mask,中间过程可选中

  1. G2.RegisterInteraction('element-multiple-selected', {
  2. start: [
  3. {trigger: 'view:mousedowm', action: 'element-range-selected:start'}
  4. {trigger: 'view:mousedowm', action: 'element-mask:start'}
  5. ],
  6. processing: [
  7. {trigger: 'view:mousemove', action: 'element-multiple-selected:selected'},
  8. {trigger: 'view:mousemove', action: 'element-mask:resize'}
  9. ],
  10. end: [
  11. {trigger: 'view:mouseup', action: 'element-range-selected:selected'},
  12. {trigger: 'view:mouseup', action: 'element-mask:hide'}
  13. ],
  14. rollback: [
  15. {trigger: 'view:click', action: 'element-range-selected:reset'}
  16. ]
  17. });

显示隐藏

Element 的显示隐藏一般发生在过滤时,如果发生数据过滤,则会整体更新,所以这里并不是数据过滤,而是组件导致的图形过滤。

  1. class ElementFilterAction extends Action {
  2. filter() {}
  3. reset() {}
  4. }
  5. class ElementFilterByXXXAction extends Action {
  6. filter() {}
  7. reset() {}
  8. }
  9. class ElementRangeFilterAction extends Action {
  10. filter() {}
  11. reset() {}
  12. }

通过图例过滤元素

  1. G2.RegisterInteraction('element-legend-filter', {
  2. showEnable: [
  3. {trigger: 'legend-item:mouseenter', action: 'cursor:pointer'},
  4. {trigger: 'legend-item:mouseleave', action: 'cursor:default'}
  5. ],
  6. start: [
  7. {trigger: 'legend-item:checkedchanged', action: 'element-filter:filter'}
  8. ],
  9. rollback: [
  10. {trigger: 'legend-item:dblclick', action: 'element-filter:reset'}
  11. ]
  12. });

通过连续图例过滤

  1. G2.RegisterInteraction('element-legend-slider-filter', {
  2. start: [
  3. {trigger: 'legend:valuechanged', action: 'element-filter:filter'}
  4. ]
  5. });

通过 axis 上的 label 过滤

  1. G2.RegisterInteraction('element-axis-filter', {
  2. showEnable: [
  3. {trigger: 'axis-label:mouseenter', action: 'cursor:pointer'},
  4. {trigger: 'axis-label:mouseleave', action: 'cursor:default'}
  5. ],
  6. start: [
  7. {trigger: 'axis-label:checkedchanged', action: 'element-filter:filter'}
  8. ],
  9. rollback: [
  10. {trigger: 'axis-label:dblclick', action: 'element-filter:reset'}
  11. ]
  12. });

通过框选过滤

  1. G2.RegisterInteraction('element-axis-filter', {
  2. showEnable: [
  3. {trigger: 'view:mouseenter', action: 'cursor:cross'},
  4. {trigger: 'view:mouseleave', action: 'cursor:default'}
  5. ],
  6. start: [
  7. {trigger: 'view:mousedown', action: 'element-range-filter:start'},
  8. {trigger: 'view:mousedown', action: 'element-delegation:start'}
  9. ],
  10. processing: [
  11. {trigger: 'view:mousemove', action: 'element-delegation:resize'}
  12. ],
  13. end: [
  14. {trigger: 'view:mouseup', action: 'element-range-filter:filter'},
  15. {trigger: 'view:mouseup', action: 'element-delegation:hide'}
  16. ],
  17. rollback: [
  18. {trigger: 'view:dblclick', action: 'element-range-filter:reset'}
  19. ]
  20. });

拖拽&框选

  1. class ElementMove extends Action {
  2. start() {}
  3. move() {}
  4. end() {}
  5. cancel() {}
  6. }

拖拽

拖拽移动

  1. G2.RegisterInteraction('element-drag-move', {
  2. showEnable: [
  3. {trigger: 'element:mouseenter', action: 'cursor:move'},
  4. {trigger: 'element:mouseleave', action: 'cursor:default'},
  5. ],
  6. start: [
  7. {trigger: 'element:dragstart', action: 'element-move:start'},
  8. ],
  9. processing: [
  10. {trigger: 'element:drag', action: 'element-move:move'},
  11. ],
  12. end: [
  13. {trigger: 'element:dragend', action: 'element-move:end'},
  14. ],
  15. rollback: [
  16. {trigger: 'element:dblclick', action: 'element-move:reset'},
  17. ]
  18. });

拖拽合并

  1. class ElementCombineAction extends Action {
  2. from() {}
  3. to() {}
  4. combine() {}
  5. cancel() {}
  6. reset() {}
  7. }
  8. G2.RegisterAction('element-combine', ElementCombineAction);
  1. G2.RegisterInteraction('element-drag-move', {
  2. showEnable: [
  3. {trigger: 'element:mouseenter', action: 'cursor:move'},
  4. {trigger: 'element:mouseleave', action: 'cursor:default'},
  5. ],
  6. start: [
  7. {trigger: 'element:dragstart', action: 'element-combine:from'},
  8. {trigger: 'element:dragstart', action: 'element-delegation:show'},
  9. ],
  10. processing: [
  11. {trigger: 'element:dragenter', action: 'element-combine:to'},
  12. {trigger: 'element:drag', action: 'element-delegation:move'},
  13. ],
  14. end: [
  15. {trigger: 'element:drop', action: 'element-combine:combine'},
  16. {trigger: 'element:dragend', action: 'element-delegation:hide'},
  17. {trigger: 'element:dragend', isEnable(context) {
  18. return !context.event.element; // 没有drop 到其他 element 时取消
  19. }, action: 'element-combine:cancel'},
  20. ],
  21. rollback: [
  22. {trigger: 'element:dblclick', action: 'element-move:reset'},
  23. ]
  24. });

拖拽删除

  1. class ElementDeleteAction extends Action {
  2. start() {}
  3. delete() {}
  4. reset() {}
  5. }
  1. G2.RegisterInteraction('element-drag-move', {
  2. showEnable: [
  3. {trigger: 'element:mouseenter', action: 'cursor:move'},
  4. {trigger: 'element:mouseleave', action: 'cursor:default'},
  5. ],
  6. start: [
  7. {trigger: 'element:dragstart', action: 'element-delegation:show'},
  8. {trigger: 'element:dragstart', action: 'element-delete:start'},
  9. ],
  10. processing: [
  11. {trigger: 'element:drag', action: 'element-delegation:move'},
  12. ],
  13. end: [
  14. {trigger: 'element:dragend', action: 'element-delegation:hide'},
  15. {trigger: 'recycle:drop', action: 'element-delete:delete'},
  16. ],
  17. rollback: [
  18. {trigger: 'recycle-icon:dblclick', action: 'element-delete:reset'},
  19. ]
  20. });

拖拽排序

  1. class ElementDragInsert extends Action {
  2. start() {}
  3. insert() {}
  4. reset() {}
  5. }
  1. G2.RegisterInteraction('element-drag-move', {
  2. showEnable: [
  3. {trigger: 'element:mouseenter', action: 'cursor:move'},
  4. {trigger: 'element:mouseleave', action: 'cursor:default'},
  5. ],
  6. start: [
  7. {trigger: 'element:dragstart', action: 'element-delegation:show'},
  8. {trigger: 'element:dragstart', action: 'element-drag-insert:start'},
  9. ],
  10. processing: [
  11. {trigger: 'element:drag', action: 'element-delegation:move'},
  12. ],
  13. end: [
  14. {trigger: 'element:dragend', action: 'element-delegation:hide'},
  15. {trigger: 'element:dragend', action: 'element-drag-insert:insert'},
  16. ],
  17. rollback: [
  18. {trigger: 'element:dblclick', action: 'element-delete:reset'},
  19. ]
  20. });

拖拽变大、变小

不同 Geometry 的改变大小的方式并不一致,可以定义一个 Action ,也可以拆分成多个,这里我们按照多个来实现:

  1. class LineResizeAction extends Action {
  2. start() {}
  3. resize() {}
  4. end() {}
  5. reset {}
  6. }
  7. class PointResizeAction extends Action {
  8. start() {}
  9. resize() {}
  10. end() {}
  11. reset {}
  12. }
  13. class IntervalResizeAction extends Action {
  14. start() {}
  15. resize() {}
  16. end() {}
  17. reset {}
  18. }
  19. class ElementReizeIcon extends Action {
  20. show() {}
  21. move() {}
  22. hide() {}
  23. }
  1. G2.RegisterInteraction('bubble-drag-resize', {
  2. showEnable: [
  3. {trigger: 'point:mouseeneter', action: 'element-resize-icon:show'},
  4. {trigger: 'point:mouseleave', action: 'element-resize-icon:hide'},
  5. ],
  6. start: [
  7. {trigger: 'element-resize-icon:dragstart', action: 'point-resize:start'}
  8. ],
  9. processing: [
  10. {trigger: 'element-resize-icon:drag', action: 'element-resize-icon::move'},
  11. {trigger: 'element-resize-icon:drag', action: 'point-resize:resize'}
  12. ],
  13. end: [
  14. {trigger: 'element-resize-icon:dragend', action: 'point-resize:end'}
  15. ],
  16. rollback: [
  17. {trigger: 'point:dblclcik', action: 'point-resize:reset'}
  18. ]
  19. });

按键

通过按键 active

编辑

输入

弹框

快捷键

复制、粘贴、剪切

总结

在写这些交互语法的时候一些感觉:

  • 一些交互过程中辅助展示的的 Action 是非常容易复用的,例如: Mask, delegation 等
  • 一些交互可以由多种触发源时,要么拆成多个Action,要么在 Action 内部判定不同的触发源
  • 交互触发的条件,可以在多个位置判定,但是需要让使用者有清晰的概念
  • 所有的交互都能转换成 trigger 和 Action,但是有一定的耦合,用户使用时往往需要查看 Action 的详细描述