fix: support mini program

This commit is contained in:
openwayne 2021-01-18 22:31:47 +08:00 committed by Yanyan Wang
parent 44cccdda96
commit 43866dbd4c
9 changed files with 45 additions and 716 deletions

View File

@ -1,231 +0,0 @@
import { G6Event, IG6GraphEvent } from '@antv/g6-core';
const { min, max, abs } = Math;
const DEFAULT_TRIGGER = 'shift';
const ALLOW_EVENTS = ['drag', 'shift', 'ctrl', 'alt', 'control'];
export default {
getDefaultCfg(): object {
return {
brushStyle: {
fill: '#EEF6FF',
fillOpacity: 0.4,
stroke: '#DDEEFE',
lineWidth: 1,
},
onSelect() {},
onDeselect() {},
selectedState: 'selected',
trigger: DEFAULT_TRIGGER,
includeEdges: true,
selectedEdges: [],
selectedNodes: [],
};
},
getEvents(): { [key in G6Event]?: string } {
// 检测输入是否合法
if (!(ALLOW_EVENTS.indexOf(this.trigger.toLowerCase()) > -1)) {
this.trigger = DEFAULT_TRIGGER;
console.warn(
"Behavior brush-select 的 trigger 参数不合法,请输入 'drag'、'shift'、'ctrl' 或 'alt'",
);
}
if (this.trigger === 'drag') {
return {
dragstart: 'onMouseDown',
drag: 'onMouseMove',
dragend: 'onMouseUp',
'canvas:click': 'clearStates',
};
}
return {
dragstart: 'onMouseDown',
drag: 'onMouseMove',
dragend: 'onMouseUp',
'canvas:click': 'clearStates',
keyup: 'onKeyUp',
keydown: 'onKeyDown',
};
},
onMouseDown(e: IG6GraphEvent) {
// 按在node上面拖动时候不应该是框选
const { item } = e;
let { brush } = this;
if (item) {
return;
}
if (this.trigger !== 'drag' && !this.keydown) {
return;
}
if (this.selectedNodes && this.selectedNodes.length !== 0) {
this.clearStates();
}
if (!brush) {
brush = this.createBrush();
}
this.originPoint = { x: e.canvasX, y: e.canvasY };
brush.attr({ width: 0, height: 0 });
brush.show();
this.dragging = true;
},
onMouseMove(e: IG6GraphEvent) {
if (!this.dragging) {
return;
}
if (this.trigger !== 'drag' && !this.keydown) {
return;
}
this.updateBrush(e);
},
onMouseUp(e: IG6GraphEvent) {
const { graph } = this;
// TODO: 触发了 canvas:click 导致 clearStates
if (!this.brush && !this.dragging) {
return;
}
if (this.trigger !== 'drag' && !this.keydown) {
return;
}
this.brush.remove(true); // remove and destroy
this.brush = null;
this.getSelectedNodes(e);
this.dragging = false;
},
clearStates() {
const { graph, selectedState } = this;
const nodes = graph.findAllByState('node', selectedState);
const edges = graph.findAllByState('edge', selectedState);
nodes.forEach((node) => graph.setItemState(node, selectedState, false));
edges.forEach((edge) => graph.setItemState(edge, selectedState, false));
this.selectedNodes = [];
this.selectedEdges = [];
if (this.onDeselect) {
this.onDeselect(this.selectedNodes, this.selectedEdges);
}
graph.emit('nodeselectchange', {
selectedItems: {
nodes: [],
edges: [],
},
select: false,
});
},
getSelectedNodes(e: IG6GraphEvent) {
const { graph, originPoint, shouldUpdate } = this;
const state = this.selectedState;
const p1 = { x: e.x, y: e.y };
const p2 = graph.getPointByCanvas(originPoint.x, originPoint.y);
const left = min(p1.x, p2.x);
const right = max(p1.x, p2.x);
const top = min(p1.y, p2.y);
const bottom = max(p1.y, p2.y);
const selectedNodes = [];
const selectedIds = [];
graph.getNodes().forEach((node) => {
const bbox = node.getBBox();
if (
bbox.centerX >= left &&
bbox.centerX <= right &&
bbox.centerY >= top &&
bbox.centerY <= bottom
) {
if (shouldUpdate(node, 'select')) {
selectedNodes.push(node);
const model = node.getModel();
selectedIds.push(model.id);
graph.setItemState(node, state, true);
}
}
});
const selectedEdges = [];
if (this.includeEdges) {
// 选中边边的source和target都在选中的节点中时才选中
selectedNodes.forEach((node) => {
const edges = node.getOutEdges();
edges.forEach((edge) => {
const model = edge.getModel();
const { source, target } = model;
if (
selectedIds.includes(source) &&
selectedIds.includes(target) &&
shouldUpdate(edge, 'select')
) {
selectedEdges.push(edge);
graph.setItemState(edge, this.selectedState, true);
}
});
});
}
this.selectedEdges = selectedEdges;
this.selectedNodes = selectedNodes;
if (this.onSelect) {
this.onSelect(selectedNodes, selectedEdges);
}
graph.emit('nodeselectchange', {
selectedItems: {
nodes: selectedNodes,
edges: selectedEdges,
},
select: true,
});
},
createBrush() {
const self = this;
const brush = self.graph.get('canvas').addShape('rect', {
attrs: self.brushStyle,
capture: false,
name: 'brush-shape',
});
this.brush = brush;
return brush;
},
updateBrush(e: IG6GraphEvent) {
const { originPoint } = this;
this.brush.attr({
width: abs(e.canvasX - originPoint.x),
height: abs(e.canvasY - originPoint.y),
x: min(e.canvasX, originPoint.x),
y: min(e.canvasY, originPoint.y),
});
},
onKeyDown(e: IG6GraphEvent) {
const code = e.key;
if (!code) {
return;
}
const triggerLowerCase = this.trigger.toLowerCase();
const codeLowerCase = code.toLowerCase();
// 按住 control 键时,允许用户设置 trigger 为 ctrl
if (
codeLowerCase === triggerLowerCase ||
(codeLowerCase === 'control' && triggerLowerCase === 'ctrl') ||
(codeLowerCase === 'ctrl' && triggerLowerCase === 'control')
) {
this.keydown = true;
} else {
this.keydown = false;
}
},
onKeyUp() {
if (this.brush) {
// 清除所有选中状态后设置拖得动状态为false并清除框选的brush
this.brush.remove(true);
this.brush = null;
this.dragging = false;
}
this.keydown = false;
},
};

View File

@ -1,23 +0,0 @@
import base from './tooltip-base';
import { G6Event, EdgeConfig } from '@antv/g6-core';
export default {
getDefaultCfg(): object {
return {
item: 'edge',
offset: 12,
formatText(model: EdgeConfig) {
return `source: ${model.source} target: ${model.target}`;
},
};
},
getEvents(): { [key in G6Event | 'afterremoveitem']?: string } {
return {
'edge:mouseenter': 'onMouseEnter',
'edge:mouseleave': 'onMouseLeave',
'edge:mousemove': 'onMouseMove',
afterremoveitem: 'onMouseLeave',
};
},
...base,
};

View File

@ -1,6 +1,5 @@
import { each } from '@antv/util';
import { registerBehavior } from '@antv/g6-core';
// import Behavior from './behavior';
import DragCanvas from './drag-canvas';
import DragNode from './drag-node';
import ActivateRelations from './activate-relations';
@ -10,7 +9,7 @@ import CollapseExpand from './collapse-expand';
import DragCombo from './drag-combo';
import CollapseExpandCombo from './collapse-expand-combo';
import CreateEdge from './create-edge';
import ShortcutsCall from './shortcuts-call';
import mobileBehavior from './mobile-behavior';
const behaviors = {
'drag-canvas': DragCanvas,
@ -22,11 +21,10 @@ const behaviors = {
'drag-combo': DragCombo,
'collapse-expand-combo': CollapseExpandCombo,
'create-edge': CreateEdge,
'shortcuts-call': ShortcutsCall,
};
each(behaviors, (behavior, type: string) => {
registerBehavior(type, behavior);
registerBehavior(type, { ...behavior, ...mobileBehavior });
});
// export default Behavior;

View File

@ -1,251 +0,0 @@
import { G6Event, IG6GraphEvent, IPoint, Item } from '@antv/g6-core';
import Util from '../util';
const { isPolygonsIntersect, pathToPoints } = Util;
const DEFAULT_TRIGGER = 'shift';
const ALLOW_EVENTS = ['drag', 'shift', 'ctrl', 'alt', 'control'];
const isItemIntersecPolygon = (item: Item, polyPoints: number[][]) => {
let shapePoints;
const shape = item.getKeyShape();
if (item.get('type') === 'path') {
shapePoints = pathToPoints(shape.attr('path'));
} else {
const shapeBBox = shape.getCanvasBBox();
shapePoints = [
[shapeBBox.minX, shapeBBox.minY],
[shapeBBox.maxX, shapeBBox.minY],
[shapeBBox.maxX, shapeBBox.maxY],
[shapeBBox.minX, shapeBBox.maxY],
];
}
return isPolygonsIntersect(polyPoints, shapePoints);
};
export default {
getDefaultCfg(): object {
return {
delegateStyle: {
fill: '#EEF6FF',
fillOpacity: 0.4,
stroke: '#DDEEFE',
lineWidth: 1,
},
onSelect() {},
onDeselect() {},
selectedState: 'selected',
trigger: DEFAULT_TRIGGER,
includeEdges: true,
selectedEdges: [],
selectedNodes: [],
// multiple: false,
};
},
getEvents(): { [key in G6Event]?: string } {
// 检测输入是否合法
if (!(ALLOW_EVENTS.indexOf(this.trigger.toLowerCase()) > -1)) {
this.trigger = DEFAULT_TRIGGER;
console.warn(
"Behavior lasso-select 的 trigger 参数不合法,请输入 'drag'、'shift'、'ctrl' 或 'alt'",
);
}
if (this.trigger === 'drag') {
return {
dragstart: 'onDragStart',
drag: 'onDragMove',
dragend: 'onDragEnd',
'canvas:click': 'clearStates',
};
}
return {
dragstart: 'onDragStart',
drag: 'onDragMove',
dragend: 'onDragEnd',
keyup: 'onKeyUp',
keydown: 'onKeyDown',
'canvas:click': 'clearStates',
};
},
onDragStart(e: IG6GraphEvent) {
let { lasso } = this;
const { item } = e;
// 排除在节点上拖动
if (item) {
return;
}
if (this.trigger !== 'drag' && !this.keydown) {
return;
}
if (this.selectedNodes && this.selectedNodes.length !== 0) {
this.clearStates();
}
if (!lasso) {
lasso = this.createLasso();
}
this.dragging = true;
this.originPoint = { x: e.x, y: e.y };
this.points.push(this.originPoint);
lasso.show();
},
onDragMove(e: IG6GraphEvent) {
if (!this.dragging) {
return;
}
if (this.trigger !== 'drag' && !this.keydown) {
return;
}
this.points.push({ x: e.x, y: e.y });
this.updateLasso(e);
},
onDragEnd(e: IG6GraphEvent) {
if (!this.lasso && !this.dragging) {
return;
}
if (this.trigger !== 'drag' && !this.keydown) {
return;
}
this.points.push(this.originPoint);
this.getSelectedItems();
this.lasso.remove(true);
this.lasso = null;
this.points = [];
this.dragging = false;
},
getLassoPath() {
const points: IPoint[] = this.points;
const path = [];
if (points.length) {
points.forEach((point, index) => {
if (index === 0) {
path.push(['M', point.x, point.y]);
} else {
path.push(['L', point.x, point.y]);
}
});
path.push(['L', points[0].x, points[0].y]);
}
return path;
},
clearStates() {
const { graph, selectedState } = this;
const nodes = graph.findAllByState('node', selectedState);
const edges = graph.findAllByState('edge', selectedState);
nodes.forEach((node) => graph.setItemState(node, selectedState, false));
edges.forEach((edge) => graph.setItemState(edge, selectedState, false));
if (this.onDeselect) {
this.onDeselect(this.selectedNodes, this.selectedEdges);
}
this.selectedNodes = [];
this.selectedEdges = [];
graph.emit('nodeselectchange', {
selectedItems: {
nodes: [],
edges: [],
},
select: false,
});
},
getSelectedItems() {
const { graph, shouldUpdate } = this;
const lassoContour = this.points.map((point) => [
graph.getCanvasByPoint(point.x, point.y).x,
graph.getCanvasByPoint(point.x, point.y).y,
]);
const state = this.selectedState;
const selectedNodes = [];
const selectedIds = [];
graph.getNodes().forEach((node) => {
if (isItemIntersecPolygon(node, lassoContour)) {
if (shouldUpdate(node, 'select')) {
selectedNodes.push(node);
const model = node.getModel();
selectedIds.push(model.id);
graph.setItemState(node, state, true);
}
}
});
const selectedEdges = [];
if (this.includeEdges) {
// 选中边边的source和target都在选中的节点中时才选中
selectedNodes.forEach((node) => {
const edges = node.getOutEdges();
edges.forEach((edge) => {
const model = edge.getModel();
const { source, target } = model;
if (
selectedIds.includes(source) &&
selectedIds.includes(target) &&
shouldUpdate(edge, 'select')
) {
selectedEdges.push(edge);
graph.setItemState(edge, this.selectedState, true);
}
});
});
}
this.selectedEdges = selectedEdges;
this.selectedNodes = selectedNodes;
if (this.onSelect) {
this.onSelect(selectedNodes, selectedEdges);
}
graph.emit('nodeselectchange', {
selectedItems: {
nodes: selectedNodes,
edges: selectedEdges,
},
select: true,
});
},
createLasso() {
const self = this;
const lasso = self.graph.get('delegateGroup').addShape('path', {
attrs: {
path: [],
...self.delegateStyle,
},
capture: false,
name: 'lasso-shape',
});
this.lasso = lasso;
this.points = [];
return lasso;
},
updateLasso(e: IG6GraphEvent) {
const self = this;
this.lasso.attr({
path: self.getLassoPath(),
});
},
onKeyDown(e: IG6GraphEvent) {
const code = e.key;
if (!code) {
return;
}
// if (this.selectedNodes && this.selectedNodes.length !== 0) {
// this.clearStates();
// }
if (code.toLowerCase() === this.trigger.toLowerCase()) {
this.keydown = true;
} else {
this.keydown = false;
}
},
onKeyUp() {
if (this.lasso) {
// 清除所有选中状态后设置拖得动状态为false并清除框选的lasso
this.lasso.remove(true);
this.lasso = null;
this.points = [];
this.dragging = false;
}
this.keydown = false;
},
};

View File

@ -0,0 +1,22 @@
import { each } from '@antv/util';
import { IAbstractGraph, G6Event } from '@antv/g6-core';
export default {
/**
* auto bind events when register behavior
* @param graph Graph instance
*/
bind(graph: IAbstractGraph) {
const { events } = this;
each(events, (handler: () => void, event: G6Event) => {
graph.on(event, handler);
});
},
unbind(graph: IAbstractGraph) {
const { events } = this;
each(events, (handler: () => void, event: G6Event) => {
graph.off(event, handler);
});
},
};

View File

@ -1,94 +0,0 @@
import { G6Event, IG6GraphEvent } from '@antv/g6-core';
const DEFAULT_TRIGGER = 'ctrl';
const ALLOW_EVENTS = ['shift', 'ctrl', 'alt', 'control'];
const DEFAULT_COMBINED_KEY = '1';
export default {
getDefaultCfg(): object {
return {
trigger: DEFAULT_TRIGGER,
combinedKey: DEFAULT_COMBINED_KEY,
functionName: 'fitView',
functionParams: [],
};
},
getEvents(): { [key in G6Event]?: string } {
// 检测输入是否合法
if (!(ALLOW_EVENTS.indexOf(this.trigger.toLowerCase()) > -1)) {
this.trigger = DEFAULT_TRIGGER;
console.warn(
`Behavior shortcuts-fit-view 的 trigger 参数 '${this.trigger}' 不合法,请输入 'drag'、'shift'、'ctrl' 或 'alt'`,
);
}
if (this.combinedKey === this.trigger) {
this.combinedKey = undefined;
}
return {
keyup: 'onKeyUp',
keydown: 'onKeyDown',
};
},
onKeyDown(e: IG6GraphEvent) {
const code = e.key;
if (!code) {
return;
}
const triggerLowerCase = this.trigger.toLowerCase();
const codeLowerCase = code.toLowerCase();
// 按住 control 键时,允许用户设置 trigger 为 ctrl
if (!this.triggerKeydown) {
if (
codeLowerCase === triggerLowerCase ||
(codeLowerCase === 'control' && triggerLowerCase === 'ctrl') ||
(codeLowerCase === 'ctrl' && triggerLowerCase === 'control')
) {
this.triggerKeydown = true;
} else {
this.triggerKeydown = false;
}
}
const { graph } = this;
if (!graph[this.functionName]) {
console.warn(
`Behavior shortcuts-fit-view 的 functionName 参数 '${this.functionName}' 不合法,它不是 Graph 的一个函数名`,
);
return {};
}
// 未配置 combinedKey直接 fitView
if (this.triggerKeydown && !this.combinedKey) {
if (this.functionParams && this.functionParams.length)
graph[this.functionName](...this.functionParams);
else graph[this.functionName]();
return;
}
const combinedKeyLowerCase = this.combinedKey.toLowerCase();
if (this.triggerKeydown) {
if (
codeLowerCase === combinedKeyLowerCase ||
(codeLowerCase === 'control' && combinedKeyLowerCase === 'ctrl') ||
(codeLowerCase === 'ctrl' && combinedKeyLowerCase === 'control')
) {
if (this.functionParams && this.functionParams.length)
graph[this.functionName](...this.functionParams);
else graph[this.functionName]();
}
}
},
onKeyUp() {
if (this.brush) {
// 清除所有选中状态后设置拖得动状态为false并清除框选的brush
this.brush.remove(true);
this.brush = null;
this.dragging = false;
}
this.triggerKeydown = false;
},
};

View File

@ -1,90 +0,0 @@
import { modifyCSS, createDom } from '@antv/dom-util';
import { IG6GraphEvent } from '@antv/g6-core';
export default {
onMouseEnter(e: IG6GraphEvent) {
const { item } = e;
this.currentTarget = item;
this.showTooltip(e);
this.graph.emit('tooltipchange', { item: e.item, action: 'show' });
},
onMouseMove(e: IG6GraphEvent) {
if (!this.shouldUpdate(e)) {
this.hideTooltip();
return;
}
if (!this.currentTarget || e.item !== this.currentTarget) {
return;
}
this.updatePosition(e);
},
onMouseLeave(e: IG6GraphEvent) {
if (!this.shouldEnd(e)) {
return;
}
this.hideTooltip();
this.graph.emit('tooltipchange', { item: this.currentTarget, action: 'hide' });
this.currentTarget = null;
},
showTooltip(e: IG6GraphEvent) {
let { container } = this;
if (!e.item || e.item.destroyed) {
return;
}
if (!container) {
container = this.createTooltip(this.graph.get('canvas'));
this.container = container;
}
const text = this.formatText(e.item.get('model'), e);
container.innerHTML = text;
modifyCSS(this.container, { visibility: 'visible' });
this.updatePosition(e);
},
hideTooltip() {
modifyCSS(this.container, {
visibility: 'hidden',
});
},
updatePosition(e: IG6GraphEvent) {
const shouldBegin = this.get('shouldBegin');
const { width, height, container, graph } = this;
if (!shouldBegin(e)) {
modifyCSS(container, {
visibility: 'hidden',
});
return;
}
const point = graph.getPointByClient(e.clientX, e.clientY);
let { x, y } = graph.getCanvasByPoint(point.x, point.y);
const bbox = container.getBoundingClientRect();
if (x > width / 2) {
x -= bbox.width;
} else {
x += this.offset;
}
if (y > height / 2) {
y -= bbox.height;
} else {
y += this.offset;
}
const left = `${x}px`;
const top = `${y}px`;
modifyCSS(this.container, { left, top, visibility: 'visible' });
},
createTooltip(canvas): HTMLElement {
const el = canvas.get('el');
el.style.position = 'relative';
const container = createDom(`<div class="g6-tooltip g6-${this.item}-tooltip"></div>`);
el.parentNode.appendChild(container);
modifyCSS(container, {
position: 'absolute',
visibility: 'visible',
});
this.width = canvas.get('width');
this.height = canvas.get('height');
this.container = container;
this.graph.get('tooltips').push(container);
return container;
},
};

View File

@ -1,23 +0,0 @@
import { G6Event } from '@antv/g6-core';
import base from './tooltip-base';
export default {
getDefaultCfg(): object {
return {
item: 'node',
offset: 12,
formatText(model) {
return model.label;
},
};
},
getEvents(): { [key in G6Event | 'afterremoveitem']?: string } {
return {
'node:mouseenter': 'onMouseEnter',
'node:mouseleave': 'onMouseLeave',
'node:mousemove': 'onMouseMove',
afterremoveitem: 'onMouseLeave',
};
},
...base,
};

View File

@ -1,4 +1,5 @@
import { Canvas as GMobileCanvas } from '@antv/g-mobile';
import { ICanvas, IGroup, Point } from '@antv/g-base';
import { mat3 } from '@antv/matrix-util';
import { clone, deepMix, each, isString, isNumber } from '@antv/util';
import { IGraph, DataUrlType } from '../interface/graph';
@ -28,6 +29,11 @@ export default class Graph extends AbstractGraph implements IGraph {
this.destroyed = false;
}
public eventHandler(event) {
const canvas: GMobileCanvas = this.get('canvas');
canvas.registerEventCallback.bind(canvas)(event);
}
protected initLayoutController() {
const layoutController = new LayoutController(this);
this.set({
@ -41,6 +47,9 @@ export default class Graph extends AbstractGraph implements IGraph {
eventController,
});
if (this.get('context')) {
return;
}
const canvas: GMobileCanvas = this.get('canvas');
const canvasDom = canvas.get('el');
'touchstart touchmove touchend touchcancel'.split(' ').forEach((key) => {
@ -609,4 +618,16 @@ export default class Graph extends AbstractGraph implements IGraph {
super.destroy();
}
// 初始化所有 Group
protected initGroups(): void {
const canvas: GMobileCanvas = this.get('canvas');
const group: IGroup = canvas.addGroup({
id: `root`,
className: Global.rootContainerClassName,
});
this.set('group', group);
}
}