mirror of
https://gitee.com/antv/g6.git
synced 2024-11-30 10:48:24 +08:00
feat: refactor event controller and graph
This commit is contained in:
parent
274b04fed8
commit
646676f8eb
@ -14,7 +14,7 @@ module.exports = {
|
||||
moduleDirectories: [ 'node_modules', 'src' ],
|
||||
moduleFileExtensions: [ 'js', 'ts', 'json' ],
|
||||
moduleNameMapper: {
|
||||
'@g6/(.*)': '<rootDir>/src/$1',
|
||||
'@g6/types': '<rootDir>/types'
|
||||
'@g6/types': '<rootDir>/types',
|
||||
'@g6/(.*)': '<rootDir>/src/$1'
|
||||
}
|
||||
};
|
||||
|
@ -33,6 +33,7 @@
|
||||
"dist": "webpack --config webpack.config.js --mode production"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/dom-util": "^2.0.1",
|
||||
"@antv/event-emitter": "~0.1.0",
|
||||
"@antv/g-base": "^0.1.0-beta.10",
|
||||
"@antv/g-canvas": "^0.1.0-beta.11",
|
||||
|
@ -1,9 +1,10 @@
|
||||
import each from '@antv/util/lib/each'
|
||||
import wrapBehavior from '@antv/util/lib/wrap-behavior'
|
||||
import { IBehavior } from '@g6/interface/behavior';
|
||||
import { IGraph } from '@g6/interface/graph'
|
||||
import { G6Event } from '@g6/types';
|
||||
|
||||
export default class BehaviorOption {
|
||||
export default class BehaviorOption implements IBehavior {
|
||||
private _events = null
|
||||
private graph = null
|
||||
private _cfg
|
||||
@ -21,7 +22,7 @@ export default class BehaviorOption {
|
||||
}
|
||||
}
|
||||
|
||||
private getDefaultCfg() {
|
||||
public getDefaultCfg() {
|
||||
return {
|
||||
|
||||
}
|
||||
|
240
src/graph/controller/event.ts
Normal file
240
src/graph/controller/event.ts
Normal file
@ -0,0 +1,240 @@
|
||||
import addEventListener from '@antv/dom-util/lib/add-event-listener'
|
||||
import Canvas from '@antv/g-base/lib/abstract/canvas';
|
||||
import Group from '@antv/g-canvas/lib/group';
|
||||
import ShapeBase from '@antv/g-canvas/lib/shape/base';
|
||||
import each from '@antv/util/lib/each'
|
||||
import isNil from '@antv/util/lib/is-nil';
|
||||
import wrapBehavior from '@antv/util/lib/wrap-behavior';
|
||||
import { IGraph } from '@g6/interface/graph';
|
||||
import { IItem } from '@g6/interface/item';
|
||||
import { G6Event, IG6GraphEvent, Matrix } from '@g6/types';
|
||||
import { cloneEvent } from '@g6/util/base';
|
||||
|
||||
interface IEvent {
|
||||
destroy: () => void;
|
||||
}
|
||||
|
||||
type Fun = () => void
|
||||
|
||||
const ORIGIN_MATRIX = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ];
|
||||
const MATRIX_LEN = 9;
|
||||
|
||||
export default class Event implements IEvent {
|
||||
private graph: IGraph
|
||||
private extendEvents: any[]
|
||||
private canvasHandler: Fun;
|
||||
private dragging: boolean
|
||||
private preItem: IItem
|
||||
|
||||
constructor(graph: IGraph) {
|
||||
this.graph = graph
|
||||
this.extendEvents = []
|
||||
this.dragging = false
|
||||
// this.initEvents()
|
||||
}
|
||||
|
||||
// 初始化 G6 中的事件
|
||||
private initEvents() {
|
||||
const self = this
|
||||
const graph = this.graph;
|
||||
const canvas = graph.get('canvas');
|
||||
const el = canvas.get('el');
|
||||
const extendEvents = this.extendEvents;
|
||||
const canvasHandler: Fun = wrapBehavior(self, 'onCanvasEvents') as Fun;
|
||||
const originHandler = wrapBehavior(self, 'onExtendEvents');
|
||||
const wheelHandler = wrapBehavior(self, 'onWheelEvent');
|
||||
each(G6Event, event => {
|
||||
canvas.on(event, canvasHandler);
|
||||
});
|
||||
this.canvasHandler = canvasHandler;
|
||||
extendEvents.push(addEventListener(el, 'DOMMouseScroll', wheelHandler));
|
||||
extendEvents.push(addEventListener(el, 'mousewheel', wheelHandler));
|
||||
if (typeof window !== 'undefined') {
|
||||
extendEvents.push(addEventListener(window as any, 'keydown', originHandler));
|
||||
extendEvents.push(addEventListener(window as any, 'keyup', originHandler));
|
||||
}
|
||||
}
|
||||
|
||||
// 判断 viewport 是否改变,通过和单位矩阵对比
|
||||
private isViewportChanged(matrix: Matrix) {
|
||||
for (let i = 0; i < MATRIX_LEN; i++) {
|
||||
if (matrix[i] !== ORIGIN_MATRIX[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取 shape 的 item 对象
|
||||
private getItemRoot<T extends ShapeBase>(shape: any): T {
|
||||
while (shape && !shape.get('item')) {
|
||||
shape = shape.get('parent');
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 canvas 事件
|
||||
* @param evt 事件句柄
|
||||
*/
|
||||
protected onCanvasEvents(evt: IG6GraphEvent) {
|
||||
const self = this;
|
||||
const graph = self.graph;
|
||||
const canvas: Canvas = graph.get('canvas');
|
||||
const pixelRatio: number = canvas.get('pixelRatio');
|
||||
const target = evt.target;
|
||||
const eventType = evt.type;
|
||||
/**
|
||||
* (clientX, clientY): 相对于页面的坐标;
|
||||
* (canvasX, canvasY): 相对于 <canvas> 左上角的坐标;
|
||||
* (x, y): 相对于整个画布的坐标, 与 model 的 x, y 是同一维度的。
|
||||
*/
|
||||
evt.canvasX = evt.x / pixelRatio;
|
||||
evt.canvasY = evt.y / pixelRatio;
|
||||
let point = { x: evt.canvasX, y: evt.canvasY };
|
||||
|
||||
const group: Group = graph.get('group')
|
||||
const matrix: Matrix = group.getMatrix()
|
||||
if(this.isViewportChanged(matrix)) {
|
||||
point = graph.getPointByCanvas(evt.canvasX, evt.canvasY)
|
||||
}
|
||||
|
||||
evt.x = point.x
|
||||
evt.y = point.y
|
||||
|
||||
evt.currentTarget = graph
|
||||
|
||||
if(target === canvas) {
|
||||
if (eventType === 'mousemove') {
|
||||
self.handleMouseMove(evt, 'canvas');
|
||||
}
|
||||
evt.target = canvas;
|
||||
evt.item = null;
|
||||
graph.emit(eventType, evt);
|
||||
graph.emit('canvas:' + eventType, evt);
|
||||
return;
|
||||
}
|
||||
|
||||
const itemShape = this.getItemRoot(target);
|
||||
if (!itemShape) {
|
||||
graph.emit(eventType, evt);
|
||||
return;
|
||||
}
|
||||
|
||||
const item: IItem = itemShape.get('item');
|
||||
if (item.destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const type = item.getType();
|
||||
|
||||
// 事件target是触发事件的Shape实例,, item是触发事件的item实例
|
||||
evt.target = target;
|
||||
evt.item = item;
|
||||
graph.emit(eventType, evt);
|
||||
|
||||
// g的事件会冒泡,如果target不是canvas,可能会引起同个节点触发多次,需要另外判断
|
||||
if (eventType === 'mouseenter' || eventType === 'mouseleave' || eventType === 'dragenter' || eventType === 'dragleave') {
|
||||
return;
|
||||
}
|
||||
|
||||
graph.emit(type + ':' + eventType, evt);
|
||||
|
||||
if (eventType === 'dragstart') {
|
||||
self.dragging = true;
|
||||
}
|
||||
if (eventType === 'dragend') {
|
||||
self.dragging = false;
|
||||
}
|
||||
if (eventType === 'mousemove') {
|
||||
self.handleMouseMove(evt, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理扩展事件
|
||||
* @param evt 事件句柄
|
||||
*/
|
||||
protected onExtendEvents(evt: IG6GraphEvent) {
|
||||
this.graph.emit(evt.type, evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理滚轮事件
|
||||
* @param evt 事件句柄
|
||||
*/
|
||||
protected onWheelEvent(evt: IG6GraphEvent) {
|
||||
if (isNil(evt.wheelDelta)) {
|
||||
evt.wheelDelta = -evt.detail;
|
||||
}
|
||||
this.graph.emit('wheel', evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理鼠标移动的事件
|
||||
* @param evt 事件句柄
|
||||
* @param type item 类型
|
||||
*/
|
||||
private handleMouseMove(evt: IG6GraphEvent, type: string) {
|
||||
const self = this;
|
||||
const graph = this.graph
|
||||
const canvas: Canvas = graph.get('canvas');
|
||||
const item = evt.target === canvas ? null : evt.item;
|
||||
const preItem = this.preItem;
|
||||
|
||||
evt = cloneEvent(evt)
|
||||
|
||||
// 从前一个item直接移动到当前item,触发前一个item的leave事件
|
||||
if (preItem && preItem !== item && !preItem.destroyed) {
|
||||
evt.item = preItem;
|
||||
self.emitCustomEvent(preItem.getType(), 'mouseleave', evt);
|
||||
if (self.dragging) {
|
||||
self.emitCustomEvent(preItem.getType(), 'dragleave', evt);
|
||||
}
|
||||
}
|
||||
|
||||
// 从一个item或canvas移动到当前item,触发当前item的enter事件
|
||||
if (item && preItem !== item) {
|
||||
evt.item = item;
|
||||
self.emitCustomEvent(type, 'mouseenter', evt);
|
||||
if (self.dragging) {
|
||||
self.emitCustomEvent(type, 'dragenter', evt);
|
||||
}
|
||||
}
|
||||
|
||||
this.preItem = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 graph 上面 emit 事件
|
||||
* @param itemType item 类型
|
||||
* @param eventType 事件类型
|
||||
* @param evt 事件句柄
|
||||
*/
|
||||
private emitCustomEvent(itemType: string, eventType: string, evt: IG6GraphEvent) {
|
||||
evt.type = eventType;
|
||||
this.graph.emit(itemType + ':' + eventType, evt);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
const graph = this.graph;
|
||||
const canvasHandler = this.canvasHandler;
|
||||
const extendEvents = this.extendEvents
|
||||
const canvas: Canvas = graph.get('canvas');
|
||||
|
||||
each(G6Event, event => {
|
||||
canvas.off(event, canvasHandler);
|
||||
});
|
||||
|
||||
each(extendEvents, event => {
|
||||
event.remove();
|
||||
});
|
||||
|
||||
this.dragging = false
|
||||
this.preItem = null
|
||||
this.extendEvents.length = 0
|
||||
this.canvasHandler = null
|
||||
}
|
||||
}
|
||||
|
@ -1 +1,3 @@
|
||||
export { default as Mode } from './mode'
|
||||
export { default as ModeController } from './mode'
|
||||
export { default as ViewController } from './view'
|
||||
export { default as EventController } from './event'
|
@ -1,19 +1,254 @@
|
||||
import EventEmitter from '@antv/event-emitter'
|
||||
import { Point } from '@antv/g-base/lib/types';
|
||||
import GCanvas from '@antv/g-canvas/lib/canvas'
|
||||
import Group from '@antv/g-canvas/lib/group';
|
||||
import { mat3 } from '@antv/matrix-util/lib';
|
||||
import clone from '@antv/util/lib/clone';
|
||||
import deepMix from '@antv/util/lib/deep-mix'
|
||||
import isPlainObject from '@antv/util/lib/is-plain-object';
|
||||
import isString from '@antv/util/lib/is-string'
|
||||
import { GraphOptions, IGraph } from '@g6/interface/graph';
|
||||
import { IItem } from '@g6/interface/item';
|
||||
import { Matrix } from '@g6/types';
|
||||
import { EdgeConfig, GraphData, GroupConfig, Matrix, NodeConfig, NodeMapConfig } from '@g6/types';
|
||||
import { translate } from '@g6/util/math'
|
||||
import { Point } from '_@antv_g-base@0.1.1@@antv/g-base/lib/types';
|
||||
import Group from '_@antv_g-canvas@0.1.1@@antv/g-canvas/lib/group';
|
||||
import { mat3 } from '_@antv_matrix-util@2.0.4@@antv/matrix-util/lib';
|
||||
import clone from '_@antv_util@2.0.6@@antv/util/lib/clone';
|
||||
import isPlainObject from '_@antv_util@2.0.6@@antv/util/lib/is-plain-object';
|
||||
import { EventController, ModeController, ViewController } from './controller'
|
||||
interface PrivateGraphOption extends GraphOptions {
|
||||
data: GraphData;
|
||||
|
||||
// capture event
|
||||
event: boolean;
|
||||
|
||||
nodes: NodeConfig[];
|
||||
|
||||
edges: EdgeConfig[];
|
||||
|
||||
groups: GroupConfig[];
|
||||
|
||||
itemMap: NodeMapConfig;
|
||||
|
||||
callback: () => void;
|
||||
|
||||
// TODO 需要将暴力出去的配置和内部使用的进行区分
|
||||
groupBBoxs: any;
|
||||
|
||||
groupNodes: NodeMapConfig;
|
||||
|
||||
// TODO 需要确定下还是否需要 states
|
||||
states: any;
|
||||
}
|
||||
|
||||
export default class Graph extends EventEmitter implements IGraph {
|
||||
private _cfg: GraphOptions
|
||||
|
||||
constructor(cfg: GraphOptions) {
|
||||
super()
|
||||
this._cfg = cfg
|
||||
this._cfg = deepMix(this.getDefaultCfg(), cfg)
|
||||
this.init()
|
||||
}
|
||||
|
||||
private init() {
|
||||
this.initCanvas()
|
||||
|
||||
// instance controller
|
||||
const eventController = new EventController(this)
|
||||
const viewController = new ViewController(this)
|
||||
const modeController = new ModeController(this)
|
||||
|
||||
this.set({
|
||||
eventController,
|
||||
viewController,
|
||||
modeController
|
||||
})
|
||||
}
|
||||
|
||||
private initCanvas() {
|
||||
let container: string | HTMLElement = this.get('container')
|
||||
if(isString(container)) {
|
||||
container = document.getElementById(container)
|
||||
this.set('container', container)
|
||||
}
|
||||
|
||||
if(!container) {
|
||||
throw new Error('invalid container')
|
||||
}
|
||||
|
||||
const width: number = this.get('width')
|
||||
const height: number = this.get('height')
|
||||
const pixelRatio: number = this.get('pixelRatio')
|
||||
|
||||
const canvas = new GCanvas({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
pixelRatio
|
||||
})
|
||||
|
||||
this.set('canvas', canvas)
|
||||
}
|
||||
|
||||
public getDefaultCfg(): PrivateGraphOption {
|
||||
return {
|
||||
/**
|
||||
* Container could be dom object or dom id
|
||||
*/
|
||||
container: undefined,
|
||||
|
||||
/**
|
||||
* Canvas width
|
||||
* unit pixel if undefined force fit width
|
||||
*/
|
||||
width: undefined,
|
||||
|
||||
/**
|
||||
* Canvas height
|
||||
* unit pixel if undefined force fit height
|
||||
*/
|
||||
height: undefined,
|
||||
/**
|
||||
* renderer canvas or svg
|
||||
*/
|
||||
renderer: 'canvas',
|
||||
/**
|
||||
* control graph behaviors
|
||||
*/
|
||||
modes: {},
|
||||
/**
|
||||
* 注册插件
|
||||
*/
|
||||
plugins: [],
|
||||
/**
|
||||
* source data
|
||||
*/
|
||||
data: {},
|
||||
/**
|
||||
* Fit view padding (client scale)
|
||||
*/
|
||||
fitViewPadding: 10,
|
||||
/**
|
||||
* Minimum scale size
|
||||
*/
|
||||
minZoom: 0.2,
|
||||
/**
|
||||
* Maxmum scale size
|
||||
*/
|
||||
maxZoom: 10,
|
||||
/**
|
||||
* capture events
|
||||
*/
|
||||
event: true,
|
||||
/**
|
||||
* group node & edges into different graphic groups
|
||||
*/
|
||||
groupByTypes: true,
|
||||
/**
|
||||
* determine if it's a directed graph
|
||||
*/
|
||||
directed: false,
|
||||
/**
|
||||
* when data or shape changed, should canvas draw automatically
|
||||
*/
|
||||
autoPaint: true,
|
||||
/**
|
||||
* store all the node instances
|
||||
*/
|
||||
nodes: [],
|
||||
/**
|
||||
* store all the edge instances
|
||||
*/
|
||||
edges: [],
|
||||
/**
|
||||
* all the instances indexed by id
|
||||
*/
|
||||
itemMap: {},
|
||||
/**
|
||||
* 边直接连接到节点的中心,不再考虑锚点
|
||||
*/
|
||||
linkCenter: false,
|
||||
/**
|
||||
* 默认的节点配置,data 上定义的配置会覆盖这些配置。例如:
|
||||
* defaultNode: {
|
||||
* shape: 'rect',
|
||||
* size: [60, 40],
|
||||
* style: {
|
||||
* //... 样式配置项
|
||||
* }
|
||||
* }
|
||||
* 若数据项为 { id: 'node', x: 100, y: 100 }
|
||||
* 实际创建的节点模型是 { id: 'node', x: 100, y: 100, shape: 'rect', size: [60, 40] }
|
||||
* 若数据项为 { id: 'node', x: 100, y: 100, shape: 'circle' }
|
||||
* 实际创建的节点模型是 { id: 'node', x: 100, y: 100, shape: 'circle', size: [60, 40] }
|
||||
*/
|
||||
defaultNode: {},
|
||||
/**
|
||||
* 默认边配置,data 上定义的配置会覆盖这些配置。用法同 defaultNode
|
||||
*/
|
||||
defaultEdge: {},
|
||||
/**
|
||||
* 节点默认样式,也可以添加状态样式
|
||||
* 例如:
|
||||
* const graph = new G6.Graph({
|
||||
* nodeStateStyle: {
|
||||
* selected: { fill: '#ccc', stroke: '#666' },
|
||||
* active: { lineWidth: 2 }
|
||||
* },
|
||||
* ...
|
||||
* });
|
||||
*
|
||||
*/
|
||||
nodeStateStyles: {},
|
||||
/**
|
||||
* 边默认样式,用法同nodeStateStyle
|
||||
*/
|
||||
edgeStateStyles: {},
|
||||
/**
|
||||
* graph 状态
|
||||
*/
|
||||
states: {},
|
||||
/**
|
||||
* 是否启用全局动画
|
||||
*/
|
||||
animate: false,
|
||||
/**
|
||||
* 动画设置,仅在 animate 为 true 时有效
|
||||
*/
|
||||
animateCfg: {
|
||||
/**
|
||||
* 帧回调函数,用于自定义节点运动路径,为空时线性运动
|
||||
*/
|
||||
onFrame: null,
|
||||
/**
|
||||
* 动画时长(ms)
|
||||
*/
|
||||
duration: 500,
|
||||
/**
|
||||
* 指定动画动效
|
||||
*/
|
||||
easing: 'easeLinear'
|
||||
},
|
||||
callback: null,
|
||||
/**
|
||||
* group类型
|
||||
*/
|
||||
groupType: 'circle',
|
||||
/**
|
||||
* group bbox 对象
|
||||
* @private
|
||||
*/
|
||||
groupBBoxs: {},
|
||||
/**
|
||||
* 以groupid分组的节点数据
|
||||
* @private
|
||||
*/
|
||||
groupNodes: {},
|
||||
/**
|
||||
* group 数据
|
||||
*/
|
||||
groups: [],
|
||||
/**
|
||||
* group样式
|
||||
*/
|
||||
groupStyle: {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,4 +342,44 @@ export default class Graph extends EventEmitter implements IGraph {
|
||||
this.get('canvas').draw();
|
||||
this.emit('afterpaint');
|
||||
}
|
||||
|
||||
/**
|
||||
* 将屏幕坐标转换为视口坐标
|
||||
* @param {number} clientX 屏幕x坐标
|
||||
* @param {number} clientY 屏幕y坐标
|
||||
* @return {Point} 视口坐标
|
||||
*/
|
||||
public getPointByClient(clientX: number, clientY: number): Point {
|
||||
return this.get('viewController').getPointByClient(clientX, clientY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将视口坐标转换为屏幕坐标
|
||||
* @param {number} x 视口x坐标
|
||||
* @param {number} y 视口y坐标
|
||||
* @return {Point} 视口坐标
|
||||
*/
|
||||
public getClientByPoint(x: number, y: number): Point {
|
||||
return this.get('viewController').getClientByPoint(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将画布坐标转换为视口坐标
|
||||
* @param {number} canvasX 画布 x 坐标
|
||||
* @param {number} canvasY 画布 y 坐标
|
||||
* @return {object} 视口坐标
|
||||
*/
|
||||
public getPointByCanvas(canvasX, canvasY): Point {
|
||||
return this.get('viewController').getPointByCanvas(canvasX, canvasY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将视口坐标转换为画布坐标
|
||||
* @param {number} x 视口 x 坐标
|
||||
* @param {number} y 视口 y 坐标
|
||||
* @return {object} 画布坐标
|
||||
*/
|
||||
public getCanvasByPoint(x, y): Point {
|
||||
return this.get('viewController').getCanvasByPoint(x, y);
|
||||
}
|
||||
}
|
@ -5,20 +5,29 @@ import { IGraph } from './graph';
|
||||
import { IItem } from './item';
|
||||
|
||||
export interface IBehavior {
|
||||
constructor: (cfg?: object) => void;
|
||||
// constructor: (cfg?: object) => void;
|
||||
getEvents: () => { [key in G6Event]?: string };
|
||||
shouldBegin: () => boolean;
|
||||
shouldUpdate: () => boolean;
|
||||
shouldEnd: () => boolean;
|
||||
bind: (graph: IGraph) => void;
|
||||
unbind: (graph: IGraph) => void;
|
||||
[key: string]: (...args: DefaultBehaviorType[]) => unknown;
|
||||
getDefaultCfg?: () => object;
|
||||
// [key: string]: (...args: DefaultBehaviorType[]) => unknown;
|
||||
}
|
||||
|
||||
export class G6GraphEvent extends GraphEvent implements IG6GraphEvent {
|
||||
public item: IItem
|
||||
public canvasX: number
|
||||
public canvasY: number
|
||||
public wheelDelta: number
|
||||
public detail: number
|
||||
constructor(type, event) {
|
||||
super(type, event)
|
||||
this.item = event.item
|
||||
this.canvasX = event.canvasX
|
||||
this.canvasY = event.canvasY
|
||||
this.wheelDelta = event.wheelDelta
|
||||
this.detail = event.detail
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ export interface IModeOption {
|
||||
export type IModeType = string | IModeOption
|
||||
|
||||
export interface IMode {
|
||||
default: IModeType[]
|
||||
default?: IModeType[]
|
||||
[key: string]: IModeType[]
|
||||
}
|
||||
|
||||
@ -54,8 +54,11 @@ export interface GraphOptions {
|
||||
*/
|
||||
groupByTypes?: boolean;
|
||||
|
||||
// 是否有向图
|
||||
directed?: boolean;
|
||||
|
||||
groupStyle?: {
|
||||
style: {
|
||||
style?: {
|
||||
[key: string]: ShapeStyle
|
||||
};
|
||||
};
|
||||
@ -145,10 +148,44 @@ export interface GraphOptions {
|
||||
}
|
||||
|
||||
export interface IGraph extends EventEmitter {
|
||||
getDefaultCfg(): GraphOptions;
|
||||
get<T = any>(key: string): T;
|
||||
set<T = any>(key: string | object, value?: T): Graph;
|
||||
findById(id: string): IItem;
|
||||
translate(dx: number, dy: number): void;
|
||||
zoom(ratio: number, center: Point): void;
|
||||
|
||||
/**
|
||||
* 将屏幕坐标转换为视口坐标
|
||||
* @param {number} clientX 屏幕 x 坐标
|
||||
* @param {number} clientY 屏幕 y 坐标
|
||||
* @return {Point} 视口坐标
|
||||
*/
|
||||
getPointByClient(clientX: number, clientY: number): Point;
|
||||
|
||||
/**
|
||||
* 将视口坐标转换为屏幕坐标
|
||||
* @param {number} x 视口x坐标
|
||||
* @param {number} y 视口y坐标
|
||||
* @return {object} 视口坐标
|
||||
*/
|
||||
getClientByPoint(x: number, y: number): Point;
|
||||
|
||||
/**
|
||||
* 将画布坐标转换为视口坐标
|
||||
* @param {number} canvasX 画布 x 坐标
|
||||
* @param {number} canvasY 画布 y 坐标
|
||||
* @return {Point} 视口坐标
|
||||
*/
|
||||
getPointByCanvas(canvasX: number, canvasY: number): Point;
|
||||
|
||||
/**
|
||||
* 将视口坐标转换为画布坐标
|
||||
* @param {number} x 视口 x 坐标
|
||||
* @param {number} y 视口 y 坐标
|
||||
* @return {Point} 画布坐标
|
||||
*/
|
||||
getCanvasByPoint(x: number, y: number): Point;
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,25 +1,33 @@
|
||||
import { Mode } from '../../../../src/graph/controller'
|
||||
import { ModeController } from '../../../../src/graph/controller'
|
||||
import Graph from '../../../../src/graph/graph'
|
||||
import { GraphOptions, IGraph, IModeOption } from '../../../../src/interface/graph';
|
||||
import { GraphOptions, IModeOption } from '../../../../src/interface/graph';
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.id = 'graph-spec';
|
||||
document.body.appendChild(div);
|
||||
|
||||
describe('Mode Controller', () => {
|
||||
it('signle mode', () => {
|
||||
const cfg: GraphOptions = {
|
||||
container: 'x',
|
||||
container: 'graph-spec',
|
||||
width: 200,
|
||||
height: 100
|
||||
height: 100,
|
||||
modes: {
|
||||
default: ['drag']
|
||||
}
|
||||
}
|
||||
const graph: IGraph = new Graph(cfg)
|
||||
const modeController = new Mode(graph)
|
||||
const graph: Graph = new Graph(cfg)
|
||||
const modeController = new ModeController(graph)
|
||||
expect(Object.keys(modeController.modes).length).toBe(1);
|
||||
expect(modeController.modes.default).not.toBe(undefined);
|
||||
expect(modeController.modes.default.length).toBe(0);
|
||||
console.log(modeController.modes)
|
||||
expect(modeController.modes.default[0]).toEqual({ type: 'drag' });
|
||||
expect(modeController.modes.default.length).toBe(1);
|
||||
expect(modeController.mode).toBe('default');
|
||||
})
|
||||
|
||||
it('setMode', () => {
|
||||
const cfg: GraphOptions = {
|
||||
container: 'x',
|
||||
container: 'graph-spec',
|
||||
width: 200,
|
||||
height: 100,
|
||||
modes: {
|
||||
@ -27,8 +35,8 @@ describe('Mode Controller', () => {
|
||||
edit: ['canvans', 'zoom']
|
||||
}
|
||||
}
|
||||
const graph: IGraph = new Graph(cfg)
|
||||
const modeController = new Mode(graph)
|
||||
const graph: Graph = new Graph(cfg)
|
||||
const modeController = new ModeController(graph)
|
||||
modeController.setMode('edit')
|
||||
expect(modeController.modes.edit).not.toBe(undefined)
|
||||
expect(modeController.modes.edit.length).toBe(2)
|
||||
@ -37,7 +45,7 @@ describe('Mode Controller', () => {
|
||||
|
||||
it('manipulateBehaviors', () => {
|
||||
const cfg: GraphOptions = {
|
||||
container: 'x',
|
||||
container: 'graph-spec',
|
||||
width: 200,
|
||||
height: 100,
|
||||
modes: {
|
||||
@ -45,8 +53,8 @@ describe('Mode Controller', () => {
|
||||
edit: ['canvans', 'zoom']
|
||||
}
|
||||
}
|
||||
const graph: IGraph = new Graph(cfg)
|
||||
const modeController = new Mode(graph)
|
||||
const graph: Graph = new Graph(cfg)
|
||||
const modeController = new ModeController(graph)
|
||||
modeController.manipulateBehaviors(['delete'], 'xxx', true)
|
||||
expect(Object.keys(modeController.modes).length).toBe(3)
|
||||
expect(modeController.modes.xxx).not.toBe(undefined)
|
||||
@ -66,7 +74,7 @@ describe('Mode Controller', () => {
|
||||
|
||||
it('add & remove behavior to several modes', () => {
|
||||
const cfg: GraphOptions = {
|
||||
container: 'x',
|
||||
container: 'graph-spec',
|
||||
width: 500,
|
||||
height: 500,
|
||||
pixelRatio: 2,
|
||||
@ -76,13 +84,12 @@ describe('Mode Controller', () => {
|
||||
custom2: []
|
||||
}
|
||||
}
|
||||
const graph: IGraph = new Graph(cfg)
|
||||
const modeController = new Mode(graph)
|
||||
const graph = new Graph(cfg)
|
||||
const modeController = new ModeController(graph)
|
||||
expect(Object.keys(modeController.modes).length).toBe(3);
|
||||
modeController.manipulateBehaviors([ 'aa', 'bb' ], [ 'custom1', 'custom2' ], true);
|
||||
expect(modeController.modes.custom1.length).toBe(2);
|
||||
expect(modeController.modes.custom2.length).toBe(2);
|
||||
console.log(modeController.modes)
|
||||
|
||||
const custom1: IModeOption = modeController.modes.custom1[0] as IModeOption
|
||||
const custom2: IModeOption = modeController.modes.custom1[1] as IModeOption
|
||||
|
@ -76,7 +76,7 @@ export type ModelStyle = Partial<{
|
||||
}>
|
||||
|
||||
export type Easeing =
|
||||
'easeLinear'
|
||||
| 'easeLinear'
|
||||
| 'easePolyIn'
|
||||
| 'easePolyOut'
|
||||
| 'easePolyInOut'
|
||||
@ -110,6 +110,10 @@ export interface EdgeConfig extends ModelConfig {
|
||||
controlPoints?: IPoint[];
|
||||
}
|
||||
|
||||
export interface NodeMapConfig {
|
||||
[key: string]: NodeConfig
|
||||
}
|
||||
|
||||
export interface GroupConfig {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
@ -191,4 +195,8 @@ export type IEvent = Record<G6Event, string>
|
||||
|
||||
export interface IG6GraphEvent extends GraphEvent {
|
||||
item: IItem;
|
||||
canvasX: number;
|
||||
canvasY: number;
|
||||
wheelDelta: number;
|
||||
detail: number;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user