简介
Element 的交互可以分为:
- 状态变换的交互,例如: active, selected
 - 显示隐藏
 - 拖拽、框选
 - 编辑
 - 
Element Action
基本的 Action
基本的 Action 有:
 ElementVisible
- ElementActive
 - ElementSelected
 - ElementMove
 - ElementDelegation
 - ElementResize
 - ElementFilter
 - ElementCombine
 - ElementDelete
 
Action 的扩展
每个 Action 可以扩展出更多的 Action,适应不同类型的 Element
class ElementAction extends Action {show() {}hide() {}remove() {}}class ElementResize extends Action {start() {}resize() {}end() {}reset() {}}// 扩展新的 Actionclass BubbleResize extends ElementResize {}class ElementSelected extends Action {selected() {}clear() {}}// 扩展饼图选中class PieSelected extends ElementSelected {selected() {}clear() {}}
状态变化
active
Element 的 active 有三种形态:
- 单个 Element active
 - 多个 Element 一起 active
 - 部分高亮,部分变暗
 
class ElementActiveAction extends Action {getActiveElements() {}active() {// contextconst activeElements = this.getActiveElements()activeElements.forEach(el => {el.setState('active', true);});}reset() {const activeElements = context.view.get('activeElements');activeElements.forEach(el => {el.setState('active', false);});}}class ElementHiglightAction extends ElementActiveAction {highlight() {const activeElements = this.getActiveElements()activeElements.forEach(el => {el.setState('active', true);});}reset() {}}G2.RegisterAction('A', ElementActiveAction);
hover 到 element ,单个 element active
G2.RegisterInteraction('element-active', {start: [{trigger: 'element:mouseenter', action: 'ElementActive:active'}],end: [{trigger: 'element:mouseleave', action: 'element-active:reset'}]});
hover 到 axis label 上,对应的 element 高亮
G2.RegisterInteraction('element-axis-active', {start: [{trigger: 'axis-label:mouseenter', action: 'element-active:active'}],end: [{trigger: 'axis-label:mouseleave', action: 'element-active:reset'}]});
hover 到 legend 上,对应的 element 高亮
G2.RegisterInteraction('element-legend-active', {start: [{trigger: 'legend-item:mouseenter', action: 'element-active:active'}],end: [{trigger: 'legend-item:mouseleave', action: 'element-active:reset'}]});
hover 到 element 上 highlight
G2.RegisterInteraction('element-higlight', {start: [{trigger: 'element:mouseenter', action: 'element-highlight:highlight'}],end: [{trigger: 'element:mouseleave', action: 'element-highlight:reset'}]});
hover 到 legend 上
G2.RegisterInteraction('element-legend-higlight', {start: [{trigger: 'legend-item:mouseenter', action: 'element-higlight:active'}],end: [{trigger: 'legend-item:mouseleave', action: 'element-higlight:reset'}]});
鼠标附近 x 轴上的多个 elmement active
鼠标附近多个 elmement 高亮,需要定义一个新的 Action
class IntervalActiveByX extends Action {active() {}reset() {}}// 可以随便命名class PointActiveByXY extends Action {active() {}reset() {}}G2.RegisterAction('interval-active-by-x', IntervalActiveByX);G2.RegisterAction('point-active-by-xy', IntervalActiveByX);
G2.RegisterInteraction('element-active-by-x', {start: [{trigger: 'view:plotenter', action: 'interval-active-by-x:active'}],processing: [{trigger: 'view:plotmove', action: 'interval-active-by-x:active'}],end: [{trigger: 'view:plotenter', action: 'interval-active-by-x:reset'}]});
x,y 最近的 Element 高亮
G2.RegisterInteraction('element-active-by-x', {start: [{trigger: 'view:plotenter', action: 'point-active-by-xy:active'}],processing: [{trigger: 'view:plotmove', action: 'point-active-by-xy:active'}],end: [{trigger: 'view:plotenter', action: 'point-active-by-xy:reset'}]});
interval 相同颜色的高亮
class IntervalActiveByColor extends Action {active() {}reset() {}}
G2.RegisterInteraction('element-active-by-x', {start: [{trigger: 'element:mouseenter', action: 'interval-active-by-color:active'}],end: [{trigger: 'element:mouseleave', action: 'interval-active-by-color:reset'}]});
selected
元素的选中包括:
- 单选
 - 多选
 - 框选
 
class ElementSinlgeSelectedAction extends Action {selected() {}cancel() {}}class ElementMultipleSelectedAction extends Action {selected() {}unselected() {}reset() {}}class ElementRangeSelectedAction extends Action {start() {}selected() {}reset() {}}class ElementMaskAction extends Action {start() {}resize() {}hide() {}}class ElementDelegationAction extends Action {show() {}move() {}hide() {}}G2.registerAction('element-single-selected', ElementSinlgeSelectedAction);G2.registerAction('element-multiple-selected', ElementMultipleSelectedAction);G2.registerAction('element-mask', ElementRangeSelectedAction);
点击单选 selected,可取消
G2.RegisterInteraction('element-selected',{start: [{trigger: 'element:click', action: 'element-single-selected:selected'}],end: [{trigger: 'element:click', action: 'element-single-selected:reset'}]});
点击单选,点击空白取消选中
G2.RegisterInteraction('element-selected',{start: [{trigger: 'element:click', action: 'element-single-selected:selected'}],end: [{trigger: 'view:click', isEnable(context) {return !context.event.element; // 未点击到时取消}, action: 'element-single-selected:reset'}]});
多选,再次点击取消
G2.RegisterInteraction('element-multiple-selected', {start: [{trigger: 'element:click', action: 'element-multiple-selected:selected'}],end: [{trigger: 'element:click', action: 'element-multiple-selected:unselected'}]});
框选,无 mask,中间过程无选中
G2.RegisterInteraction('element-multiple-selected', {start: [{trigger: 'view:mousedowm', action: 'element-range-selected:start'}],end: [{trigger: 'view:mouseup', action: 'element-range-selected:selected'}]});
框选,无 mask,中间过程可选中
G2.RegisterInteraction('element-multiple-selected', {start: [{trigger: 'view:mousedowm', action: 'element-range-selected:start'}],processing: [{trigger: 'view:mousemove', action: 'element-multiple-selected:selected'}],end: [{trigger: 'view:mouseup', action: 'element-range-selected:selected'}],rollback: [{trigger: 'view:click', action: 'element-range-selected:reset'}]});
框选,有 mask,中间过程可选中
G2.RegisterInteraction('element-multiple-selected', {start: [{trigger: 'view:mousedowm', action: 'element-range-selected:start'}{trigger: 'view:mousedowm', action: 'element-mask:start'}],processing: [{trigger: 'view:mousemove', action: 'element-multiple-selected:selected'},{trigger: 'view:mousemove', action: 'element-mask:resize'}],end: [{trigger: 'view:mouseup', action: 'element-range-selected:selected'},{trigger: 'view:mouseup', action: 'element-mask:hide'}],rollback: [{trigger: 'view:click', action: 'element-range-selected:reset'}]});
显示隐藏
Element 的显示隐藏一般发生在过滤时,如果发生数据过滤,则会整体更新,所以这里并不是数据过滤,而是组件导致的图形过滤。
class ElementFilterAction extends Action {filter() {}reset() {}}class ElementFilterByXXXAction extends Action {filter() {}reset() {}}class ElementRangeFilterAction extends Action {filter() {}reset() {}}
通过图例过滤元素
G2.RegisterInteraction('element-legend-filter', {showEnable: [{trigger: 'legend-item:mouseenter', action: 'cursor:pointer'},{trigger: 'legend-item:mouseleave', action: 'cursor:default'}],start: [{trigger: 'legend-item:checkedchanged', action: 'element-filter:filter'}],rollback: [{trigger: 'legend-item:dblclick', action: 'element-filter:reset'}]});
通过连续图例过滤
G2.RegisterInteraction('element-legend-slider-filter', {start: [{trigger: 'legend:valuechanged', action: 'element-filter:filter'}]});
通过 axis 上的 label 过滤
G2.RegisterInteraction('element-axis-filter', {showEnable: [{trigger: 'axis-label:mouseenter', action: 'cursor:pointer'},{trigger: 'axis-label:mouseleave', action: 'cursor:default'}],start: [{trigger: 'axis-label:checkedchanged', action: 'element-filter:filter'}],rollback: [{trigger: 'axis-label:dblclick', action: 'element-filter:reset'}]});
通过框选过滤
G2.RegisterInteraction('element-axis-filter', {showEnable: [{trigger: 'view:mouseenter', action: 'cursor:cross'},{trigger: 'view:mouseleave', action: 'cursor:default'}],start: [{trigger: 'view:mousedown', action: 'element-range-filter:start'},{trigger: 'view:mousedown', action: 'element-delegation:start'}],processing: [{trigger: 'view:mousemove', action: 'element-delegation:resize'}],end: [{trigger: 'view:mouseup', action: 'element-range-filter:filter'},{trigger: 'view:mouseup', action: 'element-delegation:hide'}],rollback: [{trigger: 'view:dblclick', action: 'element-range-filter:reset'}]});
拖拽&框选
class ElementMove extends Action {start() {}move() {}end() {}cancel() {}}
拖拽
拖拽移动
G2.RegisterInteraction('element-drag-move', {showEnable: [{trigger: 'element:mouseenter', action: 'cursor:move'},{trigger: 'element:mouseleave', action: 'cursor:default'},],start: [{trigger: 'element:dragstart', action: 'element-move:start'},],processing: [{trigger: 'element:drag', action: 'element-move:move'},],end: [{trigger: 'element:dragend', action: 'element-move:end'},],rollback: [{trigger: 'element:dblclick', action: 'element-move:reset'},]});
拖拽合并
class ElementCombineAction extends Action {from() {}to() {}combine() {}cancel() {}reset() {}}G2.RegisterAction('element-combine', ElementCombineAction);
G2.RegisterInteraction('element-drag-move', {showEnable: [{trigger: 'element:mouseenter', action: 'cursor:move'},{trigger: 'element:mouseleave', action: 'cursor:default'},],start: [{trigger: 'element:dragstart', action: 'element-combine:from'},{trigger: 'element:dragstart', action: 'element-delegation:show'},],processing: [{trigger: 'element:dragenter', action: 'element-combine:to'},{trigger: 'element:drag', action: 'element-delegation:move'},],end: [{trigger: 'element:drop', action: 'element-combine:combine'},{trigger: 'element:dragend', action: 'element-delegation:hide'},{trigger: 'element:dragend', isEnable(context) {return !context.event.element; // 没有drop 到其他 element 时取消}, action: 'element-combine:cancel'},],rollback: [{trigger: 'element:dblclick', action: 'element-move:reset'},]});
拖拽删除
class ElementDeleteAction extends Action {start() {}delete() {}reset() {}}
G2.RegisterInteraction('element-drag-move', {showEnable: [{trigger: 'element:mouseenter', action: 'cursor:move'},{trigger: 'element:mouseleave', action: 'cursor:default'},],start: [{trigger: 'element:dragstart', action: 'element-delegation:show'},{trigger: 'element:dragstart', action: 'element-delete:start'},],processing: [{trigger: 'element:drag', action: 'element-delegation:move'},],end: [{trigger: 'element:dragend', action: 'element-delegation:hide'},{trigger: 'recycle:drop', action: 'element-delete:delete'},],rollback: [{trigger: 'recycle-icon:dblclick', action: 'element-delete:reset'},]});
拖拽排序
class ElementDragInsert extends Action {start() {}insert() {}reset() {}}
G2.RegisterInteraction('element-drag-move', {showEnable: [{trigger: 'element:mouseenter', action: 'cursor:move'},{trigger: 'element:mouseleave', action: 'cursor:default'},],start: [{trigger: 'element:dragstart', action: 'element-delegation:show'},{trigger: 'element:dragstart', action: 'element-drag-insert:start'},],processing: [{trigger: 'element:drag', action: 'element-delegation:move'},],end: [{trigger: 'element:dragend', action: 'element-delegation:hide'},{trigger: 'element:dragend', action: 'element-drag-insert:insert'},],rollback: [{trigger: 'element:dblclick', action: 'element-delete:reset'},]});
拖拽变大、变小
不同 Geometry 的改变大小的方式并不一致,可以定义一个 Action ,也可以拆分成多个,这里我们按照多个来实现:
class LineResizeAction extends Action {start() {}resize() {}end() {}reset {}}class PointResizeAction extends Action {start() {}resize() {}end() {}reset {}}class IntervalResizeAction extends Action {start() {}resize() {}end() {}reset {}}class ElementReizeIcon extends Action {show() {}move() {}hide() {}}
G2.RegisterInteraction('bubble-drag-resize', {showEnable: [{trigger: 'point:mouseeneter', action: 'element-resize-icon:show'},{trigger: 'point:mouseleave', action: 'element-resize-icon:hide'},],start: [{trigger: 'element-resize-icon:dragstart', action: 'point-resize:start'}],processing: [{trigger: 'element-resize-icon:drag', action: 'element-resize-icon::move'},{trigger: 'element-resize-icon:drag', action: 'point-resize:resize'}],end: [{trigger: 'element-resize-icon:dragend', action: 'point-resize:end'}],rollback: [{trigger: 'point:dblclcik', action: 'point-resize:reset'}]});
按键
通过按键 active
编辑
输入
弹框
快捷键
复制、粘贴、剪切
总结
在写这些交互语法的时候一些感觉:
- 一些交互过程中辅助展示的的 Action 是非常容易复用的,例如: Mask, delegation 等
 - 一些交互可以由多种触发源时,要么拆成多个Action,要么在 Action 内部判定不同的触发源
 - 交互触发的条件,可以在多个位置判定,但是需要让使用者有清晰的概念
 - 所有的交互都能转换成 trigger 和 Action,但是有一定的耦合,用户使用时往往需要查看 Action 的详细描述
 
