自定义交互
简介
G2 4.0 最大的一个变化是所有的交互不再内置,全部通过交互语法搭配而成,前面章节介绍了交互的使用 本章介绍如何自定义交互。
自定义交互
所有内置的交互都是通过自定义交互的代码实现的,G2 住的交互的接口定义为:
G2.registerInteraction(name, InteractionClass | InteractionSteps);
G2 提供了两种注册自定交互的方式
- 基于 Interaction 基类继承自己的 Interaction 类,所以上面接口的第二个方式是一个继承 Interaction 的类
- 通过交互语法组合交互
使用类继承的方式
为了兼容 G2 3.x 的交互定义方式,在 G2 4.0 中我们依然保留了类继承的方式来实现交互,首先我们来看 Interaction 类的定义:
/*
* 交互的基类
*/
class Interaction {
/** view 或者 chart */
protected view: View;
/** 配置项 */
protected cfg: LooseObject;
constructor(view: View, cfg: LooseObject) {
this.view = view;
this.cfg = cfg;
}
public init() {
this.initEvents();
}
protected initEvents() {}
protected clearEvents() {}
public destroy() {
this.clearEvents();
}
}
继承 Interaction 时一般仅需要绑定事件、移除事件
import { Interaction } from '@antv/g2';
class MyInteraction extends Interaction {
protected initEvents() {
const view = this.view;
view.on('mousedown', ev => this.onMouseDown)
}
onMouseDown = ev => {
// do any thing
}
protected clearEvents() {
const view = this.view;
view.off('mousedown', ev => this.onMouseDown)
}
}
G2.registerInteraction('my-interaction', MyInteraction);
使用交互语法组合
在交互语法中一个交互可以由多个交互环节组成,每个交互环节可以有多个触发和反馈,所以在配置交互时可以配置每个环节,每个环节都是数组,都有 trigger 和 action
- showEnable:标识交互可以发生
- start: 交互开始
- processing: 交互持续
- end: 交互结束
- rollback: 回滚
每个交互环节有多个触发和反馈构成,每个触发和反馈是一个对象:
{
trigger: 'eventName',
action: 'actionName:method' | [] | function(context) {},
isEnable(context) {}, // 可选
callback(context) {}, // 可选
once: false // 可选,默认 false
}
其中:
- trigger 触发一个交互环节的事件名,是所有 Chart 支持的事件
action 触发的反馈,可以是字符串也可以是数组,是所有内置和用户自定义的 Action,参考 交互反馈 Action 列表 。
- 字符串由 ’actionName:method‘ 组成
- 列表时可以使用相同的 action ,也可以使用不同的 action ,例如: ['element-active:clear', 'element-active:active', 'mask:clear']
除了 trigger 和 action 之外还有其他几个属性:
- isEnable(context): 是否可以触发
- callback(context): 触发后执行完所有 action 的方法后会调用回调函数
- once: boolean, 是否在一个环节内仅能执行一次
由于交互都是有多个交互环节构成,所以在这种方式下,你进需要组合你需要的交互环节,在每个环节中指定 trigger 和 action 即可,我们以两个 Interaction 来说明
- 允许对 tooltip 进行锁定的交互
- 鼠标移动到坐标轴文本上,对应的图表元素高亮
允许锁定的 tooltip
我们可以参考默认 tooltip 的实现, 有两个环节 start 和 end
// 注册 tooltip 的 interaction
registerInteraction('tooltip', {
start: [{ trigger: 'plot:mousemove', action: 'tooltip:show' }],
end: [{ trigger: 'plot:mouseleave', action: 'tooltip:hide' }],
});
但是允许锁定 tooltip 则需要增加两个环节
- 锁定 tooltip
- 解除 tooltip 的锁定
// 注册 tooltip 的 interaction
registerInteraction('locked-tooltip', {
start: [{ trigger: 'plot:mousemove', action: 'tooltip:show' }],
processing: [
{
trigger: 'plot:click',
isEnable(context) {
return !context.view.isTooltipLocked();
},
action: context => {
context.view.lockTooltip();
},
},
{
trigger: 'plot:click',
isEnable(context) {
return context.view.isTooltipLocked();
},
action: context => {
context.view.unlockTooltip();
},
},
],
end: [{ trigger: 'plot:mouseleave', action: 'tooltip:hide' }],
});
- context 上的方法在下面定义
active 坐标轴文本
这个交互我们可以分拆为两个环节:
- 鼠标移动进入坐标轴文本,对应的坐标轴文本 active,对应的 element active
- 鼠标移动出坐标轴文本, 对应的坐标轴文本取消 active,对应的 element 取消 active
通过上面的分析,我们可以发现
- 触发对象是 axis-label,事件是 'mouseenter' 和 'mouseleave'
- 相关的 Action 是坐标轴文本 active 和图表元素 Element active 的 Action查找 [支持的 Action] 我们可以发现,这两个 Action 分别为:
- list-highlight: 列表组件 axis 和 component 的列表项 a ctive
- element-highlight:图表元素 active
最终的交互语法是:
registerInteraction('axis-label-highlight', {
start: [
{
trigger: 'axis-label:mouseenter',
action: ['list-active:active', 'element-active:active'],
},
],
end: [
{
trigger: 'axis-label:mouseleave',
action: ['list-active:reset', 'element-active:reset'],
},
],
});
Context 上下文
context 中有大量函数,可以在组合交互语法和自定 Action 时使用,contex 的定义:
/** 交互上下文的接口定义 */
export interface IInteractionContext extends LooseObject {
/**
* 当前触发的事件对象
*/
event: LooseObject;
/**
* 当前的 view
*/
view: View;
/** 交互相关的 Actions */
actions: IAction[];
/**
* 缓存属性,用于上下文传递信息
* @param key 键名
* @param value 值
*/
cache(key: string, value?: any);
/**
* 获取 action
* @param name - action 的名称
* @returns 指定 name 的 Action
*/
getAction(name): IAction;
/**
* 获取当前的点
* @returns 返回当前的点
*/
getCurrentPoint(): Point;
/**
* 获取当前的图形
*/
getCurrentShape(): IShape;
/**
* 添加 action
* @param action 指定交互 action
*/
addAction(action: IAction);
/**
* 移除 action
* @param action 移除的 action
*/
removeAction(action: IAction);
/**
* 事件触发时是否在 view 内部
*/
isInPlot();
/**
* 是否在组件的包围盒内
* @param name 组件名,可省略
*/
isInComponent(name?: string);
/**
* 是否在指定的图形内
* @param name shape 的名称
*/
isInShape(name);
/**
* 销毁
*/
destroy();
}
- 可以通过 context.isInPlot() 判定事件触发时,事件发生的位置是否在绘图区域内
- 可以通过 context.isInComponent('legend') 判定是否发生在 legend 的包围盒内
- 可以通过 context.event.target 或者 context.getCurrentShape() 获取触发的图形
自定义 Action
使用交互语法搭配交互时,需要使用到 Action,你可以从 G2 已经内置的 Action 列表 中选取,也可以选择自定义 Action。自定义 Action 有两个步骤:
- 实现 Action 的继承类
- 注册 Action
继承 Action
Action 的接口定义非常简单:
/** 交互反馈的定义 */
export interface IAction {
/**
* 交互 action (反馈)的名称
*/
name: string;
/**
* 上下文
*/
context: IInteractionContext;
/**
* 销毁函数
*/
destroy();
}
- name 名称即在交互组合中使用的名称
- destroy 方法销毁在 Action 实现中的资源(图形、数组等)
自定义 Action 时可以基于 Action 的基类 继承,也可以基于现有的任何 Action 继承,Action 中的每个方法代表一个反馈行为,实现为无参数的函数,我们来看一下 cursor Action 的实现:
/**
* 鼠标形状的 Action
*/
class CursorAction extends Action {
private setCursor(cursor) {
const view = this.context.view;
view.getCanvas().setCursor(cursor);
}
/**
* 默认光标(通常是一个箭头)
*/
public default() {
this.setCursor('default');
}
/** 光标呈现为指示链接的指针(一只手) */
public pointer() {
this.setCursor('pointer');
}
/** 此光标指示某对象可被移动。 */
public move() {
this.setCursor('move');
}
/** 光标呈现为十字线。 */
public crosshair() {
this.setCursor('crosshair');
}
/** 此光标指示程序正忙(通常是一只表或沙漏)。 */
public wait() {
this.setCursor('wait');
}
/** 此光标指示可用的帮助(通常是一个问号或一个气球)。 */
public help() {
this.setCursor('help');
}
/** 此光标指示文本。 */
public text() {
this.setCursor('text');
}
/**
* 此光标指示矩形框的边缘可被向右(东)移动。
*/
public eResize() {
this.setCursor('e-resize');
}
/**
* 此光标指示矩形框的边缘可被向左(西)移动。
*/
public wResize() {
this.setCursor('w-resize');
}
/**
* 此光标指示矩形框的边缘可被向上(北)移动。
*/
public nResize() {
this.setCursor('n-resize');
}
/**
* 此光标指示矩形框的边缘可被向下(南)移动。
*/
public sResize() {
this.setCursor('s-resize');
}
/**
* 光标指示可移动的方向 右上方(东北)
*/
public neResize() {
this.setCursor('ne-resize');
}
/**
* 光标指示可移动的方向 左上方(西北)
*/
public nwResize() {
this.setCursor('nw-resize');
}
/**
* 光标指示可移动的方向右下方(东南)
*/
public seResize() {
this.setCursor('se-resize');
}
/**
* 光标指示可移动的方向左下方(西南)
*/
public swResize() {
this.setCursor('sw-resize');
}
/**
* 光标指示可以在上下方向移动
*/
public nsResize() {
this.setCursor('ns-resize');
}
/**
* 光标指示可以在左右方向移动
*/
public ewResize() {
this.setCursor('ew-resize');
}
}
注册 Action
G2 提供了 registerAction 的接口用于注册 Action
G2.registerAction('cursor', CursorAction);