@antv/g2plot
Version:
An interactive and responsive charting library
176 lines (149 loc) • 4.68 kB
text/typescript
import { Action, Element } from '@antv/g2';
import { get } from '@antv/util';
import { Datum, Point } from '../../../../types';
import { findViewById } from '../../../../utils';
import { EDGES_VIEW_ID, NODES_VIEW_ID } from '../../constant';
export class SankeyNodeDragAction extends Action {
/**
* 是否在拖拽中的标记
*/
private isDragging = false;
/**
* 鼠标上一次的位置的坐标点
*/
private prevPoint: Point;
/**
* 之前的节点动画配置
*/
private prevNodeAnimateCfg: any;
/**
* 之前的边动画配置
*/
private prevEdgeAnimateCfg: any;
/**
* 当前拖拽的 element 索引
*/
private currentElementIdx: number;
/**
* 当前操作的是否是 element
*/
private isNodeElement() {
const shape = get(this.context, 'event.target');
if (shape) {
const element = shape.get('element');
return element && element.getModel().data.isNode;
}
return false;
}
private getNodeView() {
return findViewById(this.context.view, NODES_VIEW_ID);
}
private getEdgeView() {
return findViewById(this.context.view, EDGES_VIEW_ID);
}
/**
* 获取当前操作的 index
* @param element
*/
private getCurrentDatumIdx(element: Element) {
return this.getNodeView().geometries[0].elements.indexOf(element);
}
/**
* 点击下去,开始
*/
public start() {
// 记录开始了的状态
if (this.isNodeElement()) {
this.prevPoint = {
x: get(this.context, 'event.x'),
y: get(this.context, 'event.y'),
};
const element = this.context.event.target.get('element');
const idx = this.getCurrentDatumIdx(element);
if (idx === -1) {
return;
}
this.currentElementIdx = idx;
this.context.isDragging = true;
this.isDragging = true;
// 关闭动画并暂存配置
this.prevNodeAnimateCfg = this.getNodeView().getOptions().animate;
this.prevEdgeAnimateCfg = this.getEdgeView().getOptions().animate;
this.getNodeView().animate(false);
this.getEdgeView().animate(false);
}
}
/**
* 移动过程中,平移
*/
public translate() {
if (this.isDragging) {
const chart = this.context.view;
const currentPoint = {
x: get(this.context, 'event.x'),
y: get(this.context, 'event.y'),
};
const x = currentPoint.x - this.prevPoint.x;
const y = currentPoint.y - this.prevPoint.y;
const nodeView = this.getNodeView();
const element = nodeView.geometries[0].elements[this.currentElementIdx];
// 修改数据
if (element && element.getModel()) {
const prevDatum: Datum = element.getModel().data;
const data = nodeView.getOptions().data;
const coordinate = nodeView.getCoordinate();
const datumGap = {
x: x / coordinate.getWidth(),
y: y / coordinate.getHeight(),
};
const nextDatum = {
...prevDatum,
x: prevDatum.x.map((x: number) => (x += datumGap.x)),
y: prevDatum.y.map((y: number) => (y += datumGap.y)),
};
// 处理一下在 [0, 1] 范围
// 1. 更新 node 数据
const newData = [...data];
newData[this.currentElementIdx] = nextDatum;
nodeView.data(newData);
// 2. 更新 edge 数据
const name = prevDatum.name;
const edgeView = this.getEdgeView();
const edgeData = edgeView.getOptions().data;
edgeData.forEach((datum) => {
// 2.1 以该 node 为 source 的边,修改 [x0, x1, x2, x3] 中的 x0, x1
if (datum.source === name) {
datum.x[0] += datumGap.x;
datum.x[1] += datumGap.x;
datum.y[0] += datumGap.y;
datum.y[1] += datumGap.y;
}
// 2.2 以该 node 为 target 的边,修改 [x0, x1, x2, x3] 中的 x2, x3
if (datum.target === name) {
datum.x[2] += datumGap.x;
datum.x[3] += datumGap.x;
datum.y[2] += datumGap.y;
datum.y[3] += datumGap.y;
}
});
edgeView.data(edgeData);
// 3. 更新最新位置
this.prevPoint = currentPoint;
// node edge 都改变了,所以要从底层 render
chart.render(true);
}
}
}
/**
* 结论,清除状态
*/
public end() {
this.isDragging = false;
this.context.isDragging = false;
this.prevPoint = null;
this.currentElementIdx = null;
// 还原动画
this.getNodeView().animate(this.prevNodeAnimateCfg);
this.getEdgeView().animate(this.prevEdgeAnimateCfg);
}
}