diff --git a/CHANGELOG.md b/CHANGELOG.md index dec4eab4b6..bc3b591391 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # ChangeLog +### 4.8.4 + +- fix: error edge link positions for circle combo with size config, closes: #4193; +- fix: indented layout with different node widths, closes: #4200; +- feat: indented layout with align config to tell the node drawing alignment; + ### 4.8.3 - fix: unexpected error occurs when points of a hull are all duplicated; diff --git a/packages/core/package.json b/packages/core/package.json index 21d561c5e8..6b0d98ba80 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-core", - "version": "0.8.3", + "version": "0.8.4", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", diff --git a/packages/core/src/global.ts b/packages/core/src/global.ts index ce9e6e0bb1..835119beae 100644 --- a/packages/core/src/global.ts +++ b/packages/core/src/global.ts @@ -64,7 +64,7 @@ const colorSet = { }; export default { - version: '0.8.3', + version: '0.8.4', rootContainerClassName: 'root-container', nodeContainerClassName: 'node-container', edgeContainerClassName: 'edge-container', diff --git a/packages/core/src/item/combo.ts b/packages/core/src/item/combo.ts index b9b844c2e6..c944e55b10 100644 --- a/packages/core/src/item/combo.ts +++ b/packages/core/src/item/combo.ts @@ -28,7 +28,7 @@ export default class Combo extends Node implements ICombo { // merge graph的item样式与数据模型中的样式 const newModel = model; const size = { - r: bbox.width / 2 || Global.defaultCombo.size[0] / 2, + r: (Math.max(bbox.width, bbox.height) || Math.max(...Global.defaultCombo.size)) / 2, // bbox.width / 2 || Global.defaultCombo.size[0] / 2, width: bbox.width || Global.defaultCombo.size[0], height: bbox.height || Global.defaultCombo.size[1], }; @@ -60,33 +60,15 @@ export default class Combo extends Node implements ICombo { const bbox = getBBox(keyShape, group); bbox.centerX = (bbox.minX + bbox.maxX) / 2; bbox.centerY = (bbox.minY + bbox.maxY) / 2; - const cacheSize = this.get(CACHE_SIZE); const cacheBBox = this.get(CACHE_BBOX) || {}; const oriX = cacheBBox.x; const oriY = cacheBBox.x; - if (cacheSize) { - cacheSize.width = Math.max(cacheSize.width, bbox.width); - cacheSize.height = Math.max(cacheSize.height, bbox.height); - const type: string = keyShape.get('type'); - if (type === 'circle') { - bbox.width = cacheSize.r * 2; - bbox.height = cacheSize.r * 2; - } else { - bbox.width = cacheSize.width; - bbox.height = cacheSize.height; - } - bbox.minX = bbox.centerX - bbox.width / 2; - bbox.minY = bbox.centerY - bbox.height / 2; - bbox.maxX = bbox.centerX + bbox.width / 2; - bbox.maxY = bbox.centerY + bbox.height / 2; - } else { - bbox.width = bbox.maxX - bbox.minX; - bbox.height = bbox.maxY - bbox.minY; - bbox.centerX = (bbox.minX + bbox.maxX) / 2; - bbox.centerY = (bbox.minY + bbox.maxY) / 2; - } + bbox.width = bbox.maxX - bbox.minX; + bbox.height = bbox.maxY - bbox.minY; + bbox.centerX = (bbox.minX + bbox.maxX) / 2; + bbox.centerY = (bbox.minY + bbox.maxY) / 2; bbox.x = bbox.minX; bbox.y = bbox.minY; if (bbox.x !== oriX || bbox.y !== oriY) this.set(CACHE_ANCHOR_POINTS, null); diff --git a/packages/core/src/types/index.ts b/packages/core/src/types/index.ts index be7c826a2e..0234047461 100644 --- a/packages/core/src/types/index.ts +++ b/packages/core/src/types/index.ts @@ -326,7 +326,11 @@ export interface ModeOption { minZoom?: number; enableOptimize?: boolean; enableDebounce?: boolean; - allowDragOnItem?: boolean; + allowDragOnItem?: boolean | { + node?: boolean, + edge?: boolean, + combo?: boolean + }; optimizeZoom?: number; multiple?: boolean; activeState?: string; diff --git a/packages/core/tests/unit/graph/graph-spec.ts b/packages/core/tests/unit/graph/graph-spec.ts index de21d94122..8857812db3 100644 --- a/packages/core/tests/unit/graph/graph-spec.ts +++ b/packages/core/tests/unit/graph/graph-spec.ts @@ -1557,13 +1557,10 @@ describe('redo stack & undo stack', () => { graph.render(); it('fill undo stack', () => { - // redo 后,undo stack 有一条数据 let stackData = graph.getStackData(); let undoStack = stackData.undoStack; let redoStack = stackData.redoStack; - expect(undoStack.length).toBe(1); - expect(undoStack[0].action).toEqual('render'); - expect(undoStack[0].data.after.nodes.length).toEqual(2); + expect(undoStack.length).toBe(0); expect(redoStack.length).toBe(0); // update 后,undo stack 中有 2 条数据,一条 render,一条 update @@ -1574,7 +1571,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(2); + expect(undoStack.length).toBe(1); let firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('update'); @@ -1590,7 +1587,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(3); + expect(undoStack.length).toBe(2); firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('update'); @@ -1608,7 +1605,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(4); + expect(undoStack.length).toBe(3); firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('add'); @@ -1621,7 +1618,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(5); + expect(undoStack.length).toBe(4); firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('visible'); @@ -1632,7 +1629,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(6); + expect(undoStack.length).toBe(5); firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('delete'); @@ -1645,7 +1642,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(7); + expect(undoStack.length).toBe(6); firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('addItems'); diff --git a/packages/core/tests/unit/shape/combo-collapsed-pos-spec.ts b/packages/core/tests/unit/shape/combo-collapsed-pos-spec.ts index 42045e0f7f..cafd0d00c9 100644 --- a/packages/core/tests/unit/shape/combo-collapsed-pos-spec.ts +++ b/packages/core/tests/unit/shape/combo-collapsed-pos-spec.ts @@ -544,32 +544,32 @@ describe('hierarchy data 2: combo A has 2 children: an empty combo B, a node', ( expect(comboAModel.x).toBe(100); expect(comboAModel.y).toBe(200); // the child combo follow the parent - expect(comboBModel.x).toBe(232.75); - expect(comboBModel.y).toBe(377.75); - expect(nodeModel.x).toBe(-57.25); - expect(nodeModel.y).toBe(-2.25); + expect(comboBModel.x).toBe(232.5); + expect(comboBModel.y).toBe(377.5); + expect(nodeModel.x).toBe(-57.5); + expect(nodeModel.y).toBe(-2.5); }); it('move child empty combo B', () => { graph.updateItem('B', { x: 330, y: 120 }); expect(comboBModel.x).toBe(330); expect(comboBModel.y).toBe(120); // the sibling node is not changed - expect(nodeModel.x).toBe(-57.25); - expect(nodeModel.y).toBe(-2.25); + expect(nodeModel.x).toBe(-57.5); + expect(nodeModel.y).toBe(-2.5); // the parent combo follows the children graph.updateCombos(); - expect(comboAModel.x).toBe(148.625); - expect(comboAModel.y).toBe(71.125); + expect(comboAModel.x).toBe(148.75); + expect(comboAModel.y).toBe(71.25); }); it('move parent combo A', () => { // done graph.updateItem('A', { x: 450, y: 350 }); expect(comboAModel.x).toBe(450); expect(comboAModel.y).toBe(350); // the child follow the parent - expect(comboBModel.x).toBe(450 - 148.625 + 330); - expect(comboBModel.y).toBe(350 - 71.125 + 120); - expect(nodeModel.x).toBe(450 - 148.625 - 57.25); - expect(nodeModel.y).toBe(350 - 71.125 - 2.25); + expect(comboBModel.x).toBe(450 - 148.75 + 330); + expect(comboBModel.y).toBe(350 - 71.25 + 120); + expect(nodeModel.x).toBe(450 - 148.75 - 57.5); + expect(nodeModel.y).toBe(350 - 71.25 - 2.5); }); it('parent combo without pos, children with pos', () => { const testData = clone(data); @@ -591,8 +591,8 @@ describe('hierarchy data 2: combo A has 2 children: an empty combo B, a node', ( expect(nodeModel.x).toBe(10); expect(nodeModel.y).toBe(20); // A has no position, follows the child - expect(comboAModel.x).toBe(167.25); - expect(comboAModel.y).toBe(222.25); + expect(comboAModel.x).toBe(167.5); + expect(comboAModel.y).toBe(222.5); }); it('parent combo with pos, child combo without pos', () => { const testData = clone(data); @@ -670,16 +670,16 @@ describe('hierarchy data3: combo A has 2 children: combo B with 2 nodes, 2 nodes expect(comboAModel.x).toBe(100); expect(comboAModel.y).toBe(200); // the child combo follow the parent - expect(comboBModel.x).toBe(161.7898448916085); - expect(comboBModel.y).toBe(321.7898448916085); - expect(nodeModels[0].x).toBe(1.7898448916085101); - expect(nodeModels[0].y).toBe(41.78984489160848); - expect(nodeModels[1].x).toBe(11.78984489160851); - expect(nodeModels[1].y).toBe(51.78984489160848); - expect(nodeModels[2].x).toBe(156.7898448916085); - expect(nodeModels[2].y).toBe(316.7898448916085); - expect(nodeModels[3].x).toBe(166.7898448916085); - expect(nodeModels[3].y).toBe(326.7898448916085); + expect(comboBModel.x).toBe(161.5398448916085); + expect(comboBModel.y).toBe(321.5398448916085); + expect(nodeModels[0].x).toBe(1.5398448916085101); + expect(nodeModels[0].y).toBe(41.53984489160848); + expect(nodeModels[1].x).toBe(11.53984489160851); + expect(nodeModels[1].y).toBe(51.53984489160848); + expect(nodeModels[2].x).toBe(156.5398448916085); + expect(nodeModels[2].y).toBe(316.5398448916085); + expect(nodeModels[3].x).toBe(166.5398448916085); + expect(nodeModels[3].y).toBe(326.5398448916085); }); it('move child combo B', () => { graph.updateItem('B', { x: 330, y: 120 }); @@ -687,10 +687,10 @@ describe('hierarchy data3: combo A has 2 children: combo B with 2 nodes, 2 nodes expect(comboBModel.x).toBe(330); expect(comboBModel.y).toBe(120); // the sibling node is not changed - expect(nodeModels[0].x).toBe(1.7898448916085101); - expect(nodeModels[0].y).toBe(41.78984489160848); - expect(nodeModels[1].x).toBe(11.78984489160851); - expect(nodeModels[1].y).toBe(51.78984489160848); + expect(nodeModels[0].x).toBe(1.5398448916085101); + expect(nodeModels[0].y).toBe(41.53984489160848); + expect(nodeModels[1].x).toBe(11.53984489160851); + expect(nodeModels[1].y).toBe(51.53984489160848); // the children nodes of combo B follow B expect(nodeModels[2].x).toBe(325); expect(nodeModels[2].y).toBe(115); @@ -698,21 +698,21 @@ describe('hierarchy data3: combo A has 2 children: combo B with 2 nodes, 2 nodes expect(nodeModels[3].y).toBe(125); // the parent combo follows the children - expect(comboAModel.x).toBe(184.10507755419576); - expect(comboAModel.y).toBe(99.10507755419573); + expect(comboAModel.x).toBe(184.23007755419576); + expect(comboAModel.y).toBe(99.23007755419573); }); it('move parent combo A', (done) => { graph.updateItem('A', { x: 150, y: 150 }); expect(comboAModel.x).toBe(150); expect(comboAModel.y).toBe(150); // the child follow the parent - const dx = 150 - 184.10507755419576, dy = 150 - 99.10507755419573; + const dx = 150 - 184.23007755419576, dy = 150 - 99.23007755419573; expect(comboBModel.x).toBe(dx + 330); expect(comboBModel.y).toBe(dy + 120); - expect(nodeModels[0].x).toBe(dx + 1.7898448916085101); - expect(nodeModels[0].y).toBe(dy + 41.78984489160848); - expect(nodeModels[1].x).toBe(dx + 11.78984489160851); - expect(nodeModels[1].y).toBe(dy + 51.78984489160848); + expect(nodeModels[0].x).toBe(dx + 1.5398448916085101); + expect(nodeModels[0].y).toBe(dy + 41.53984489160848); + expect(nodeModels[1].x).toBe(dx + 11.53984489160851); + expect(nodeModels[1].y).toBe(dy + 51.53984489160848); expect(nodeModels[2].x).toBe(dx + 325); expect(nodeModels[2].y).toBe(dy + 115); expect(nodeModels[3].x).toBe(dx + 335); @@ -727,8 +727,8 @@ describe('hierarchy data3: combo A has 2 children: combo B with 2 nodes, 2 nodes graph.updateCombos(); setTimeout(() => { expect(Math.abs(comboA.getKeyShape().attr('r') - 105) < 1).toBe(true); - expect(comboAModel.x).toBe(21.092383668706375); - expect(comboAModel.y).toBe(64.09238366870638); + expect(comboAModel.x).toBe(21.154883668706375); + expect(comboAModel.y).toBe(63.654883668706375); done(); }, 500); }, 500); @@ -760,8 +760,8 @@ describe('hierarchy data3: combo A has 2 children: combo B with 2 nodes, 2 nodes expect(nodeModels[3].x).toBe(305); expect(nodeModels[3].y).toBe(405); // A has no position, follows the child - expect(comboAModel.x).toBe(238.2101551083915); - expect(comboAModel.y).toBe(278.2101551083915); + expect(comboAModel.x).toBe(238.4601551083915); + expect(comboAModel.y).toBe(278.4601551083915); }); it('parent combo with pos, child combo without pos', () => { const testData = clone(data); @@ -824,16 +824,16 @@ describe('hierarchy data3: combo A has 2 children: combo B with 2 nodes, 2 nodes setTimeout(() => { expect(comboB.isVisible()).toBe(true); // the child combo follow the parent - expect(comboBModel.x).toBe(161.7898448916085); - expect(comboBModel.y).toBe(321.7898448916085); - expect(nodeModels[0].x).toBe(1.7898448916085101); - expect(nodeModels[0].y).toBe(41.78984489160848); - expect(nodeModels[1].x).toBe(11.78984489160851); - expect(nodeModels[1].y).toBe(51.78984489160848); - expect(nodeModels[2].x).toBe(156.7898448916085); - expect(nodeModels[2].y).toBe(316.7898448916085); - expect(nodeModels[3].x).toBe(166.7898448916085); - expect(nodeModels[3].y).toBe(326.7898448916085); + expect(comboBModel.x).toBe(161.5398448916085); + expect(comboBModel.y).toBe(321.5398448916085); + expect(nodeModels[0].x).toBe(1.5398448916085101); + expect(nodeModels[0].y).toBe(41.53984489160848); + expect(nodeModels[1].x).toBe(11.53984489160851); + expect(nodeModels[1].y).toBe(51.53984489160848); + expect(nodeModels[2].x).toBe(156.5398448916085); + expect(nodeModels[2].y).toBe(316.5398448916085); + expect(nodeModels[3].x).toBe(166.5398448916085); + expect(nodeModels[3].y).toBe(326.5398448916085); graph.destroy(); done() }, 500) @@ -1011,7 +1011,7 @@ describe('hierarchy data5: combo A has 2 children: combo B with 2 nodes, 2 nodes setTimeout(() => { expect(comboModels[0].x).toBe(115.70219861834002); expect(comboModels[0].y).toBe(120); - expect(Math.abs(combos[0].getKeyShape().attr('r') - 157) < 1).toBe(true); + expect(Math.abs(combos[0].getKeyShape().attr('r') - 158) < 1).toBe(true); done(); }, 500) }); @@ -1144,7 +1144,7 @@ describe('hierarchy data6: combo A has 2 children: combo B with 2 nodes, 2 nodes setTimeout(() => { expect(comboModels[0].x).toBe(130); expect(comboModels[0].y).toBe(120); - expect(Math.abs(combos[0].getKeyShape().attr('r') - 130) < 1).toBe(true); + expect(Math.abs(combos[0].getKeyShape().attr('r') - 132) < 1).toBe(true); done(); }, 500) }); diff --git a/packages/element/package.json b/packages/element/package.json index 97425d0354..7c11ed7c14 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-element", - "version": "0.8.3", + "version": "0.8.4", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", @@ -61,7 +61,7 @@ }, "dependencies": { "@antv/g-base": "^0.5.1", - "@antv/g6-core": "0.8.3", + "@antv/g6-core": "0.8.4", "@antv/util": "~2.0.5" }, "devDependencies": { diff --git a/packages/element/src/edges/polyline.ts b/packages/element/src/edges/polyline.ts index 6c6ab600f2..c22fdb3756 100644 --- a/packages/element/src/edges/polyline.ts +++ b/packages/element/src/edges/polyline.ts @@ -88,7 +88,7 @@ registerEdge( const routeCfg = mix({}, defaultRouteCfg, cfg.routeCfg); routeCfg.offset = style.offset; - let path = (this as any).getPath(points, source, target, radius, routeCfg); + let path = (this as any).getPath(points, source, target, radius, routeCfg, !Boolean(controlPoints)); if ((isArray(path) && path.length <= 1) || (isString(path) && path.indexOf('L') === -1)) { path = 'M0 0, L0 0'; } @@ -133,7 +133,7 @@ registerEdge( const routeCfg = mix({}, defaultRouteCfg, cfg.routeCfg); routeCfg.offset = previousStyle.offset; - let path = (this as any).getPath(points, source, target, radius, routeCfg); + let path = (this as any).getPath(points, source, target, radius, routeCfg, !Boolean(controlPoints)); if ((isArray(path) && path.length <= 1) || (isString(path) && path.indexOf('L') === -1)) { path = 'M0 0, L0 0'; } @@ -170,11 +170,12 @@ registerEdge( target: INode, radius: number, routeCfg?: RouterCfg, + auto?: boolean, // whether calculate the path with A* ): Array> | string { const { offset, obstacles } = routeCfg; let simple = routeCfg.simple; // 指定了控制点 - if (!offset || points.length > 2) { + if (!offset || points.length > 2 || auto === false) { if (radius) { return getPathWithBorderRadiusByPolyline(points, radius); } diff --git a/packages/g6/package.json b/packages/g6/package.json index 8bb9a810da..3aebb7b593 100644 --- a/packages/g6/package.json +++ b/packages/g6/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6", - "version": "4.8.3", + "version": "4.8.4", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", @@ -66,7 +66,7 @@ ] }, "dependencies": { - "@antv/g6-pc": "0.8.3" + "@antv/g6-pc": "0.8.4" }, "devDependencies": { "@babel/core": "^7.7.7", diff --git a/packages/g6/src/index.ts b/packages/g6/src/index.ts index f0a744c936..6f8c7652a8 100644 --- a/packages/g6/src/index.ts +++ b/packages/g6/src/index.ts @@ -1,7 +1,7 @@ import G6 from '@antv/g6-pc'; -G6.version = '4.8.3'; +G6.version = '4.8.4'; export * from '@antv/g6-pc'; export default G6; -export const version = '4.8.3'; \ No newline at end of file +export const version = '4.8.4'; \ No newline at end of file diff --git a/packages/pc/package.json b/packages/pc/package.json index f514934e3c..1830c27c4f 100644 --- a/packages/pc/package.json +++ b/packages/pc/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-pc", - "version": "0.8.3", + "version": "0.8.4", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", @@ -75,9 +75,9 @@ "@antv/g-canvas": "^0.5.2", "@antv/g-math": "^0.1.1", "@antv/g-svg": "^0.5.1", - "@antv/g6-core": "0.8.3", - "@antv/g6-element": "0.8.3", - "@antv/g6-plugin": "0.8.3", + "@antv/g6-core": "0.8.4", + "@antv/g6-element": "0.8.4", + "@antv/g6-plugin": "0.8.4", "@antv/hierarchy": "^0.6.7", "@antv/layout": "^0.3.0", "@antv/matrix-util": "^3.1.0-beta.3", diff --git a/packages/pc/src/behavior/drag-canvas.ts b/packages/pc/src/behavior/drag-canvas.ts index 64ea86a08d..03d6fc90b9 100644 --- a/packages/pc/src/behavior/drag-canvas.ts +++ b/packages/pc/src/behavior/drag-canvas.ts @@ -1,4 +1,5 @@ import { G6Event, IG6GraphEvent } from '@antv/g6-core'; +import { isBoolean, isObject } from '@antv/util'; import { IGraph } from '../interface/graph'; import Util from '../util'; @@ -136,9 +137,7 @@ export default { } if (self.keydown) return; - const target = e.target; - const targetIsCanvas = target && target.isCanvas && target.isCanvas(); - if (!this.allowDragOnItem && !targetIsCanvas) return; + if (!this.allowDrag(e)) return; self.origin = { x: e.clientX, y: e.clientY }; self.dragging = false; @@ -194,9 +193,7 @@ export default { if (!this.mousedown) return; const { graph } = this; if (this.keydown) return; - const target = e.target; - const targetIsCanvas = target && target.isCanvas && target.isCanvas(); - if (!this.allowDragOnItem && !targetIsCanvas) return; + if (!this.allowDrag(e)) return; e = cloneEvent(e); if (!this.origin) { @@ -310,4 +307,17 @@ export default { this.dragging = false; this.dragbegin = false; }, + allowDrag(evt: IG6GraphEvent) { + const target = evt.target; + const targetIsCanvas = target && target.isCanvas && target.isCanvas(); + if (isBoolean(this.allowDragOnItem) && !this.allowDragOnItem && !targetIsCanvas) return false; + if (isObject(this.allowDragOnItem)) { + const { node, edge, combo } = this.allowDragOnItem; + const itemType = evt.item?.getType?.(); + if (!node && itemType === 'node') return false; + if (!edge && itemType === 'edge') return false; + if (!combo && itemType === 'combo') return false; + } + return true; + } }; diff --git a/packages/pc/src/behavior/scroll-canvas.ts b/packages/pc/src/behavior/scroll-canvas.ts index 75900ea252..6fe16bdda2 100644 --- a/packages/pc/src/behavior/scroll-canvas.ts +++ b/packages/pc/src/behavior/scroll-canvas.ts @@ -1,4 +1,5 @@ import { G6Event, IG6GraphEvent } from '@antv/g6-core'; +import { isBoolean, isObject } from '@antv/util'; const ALLOW_EVENTS = ['shift', 'ctrl', 'alt', 'control', 'meta']; @@ -25,9 +26,7 @@ export default { }, onWheel(ev: IG6GraphEvent) { - const target = ev.target; - const targetIsCanvas = target && target.isCanvas && target.isCanvas(); - if (!this.allowDragOnItem && !targetIsCanvas) return; + if (!this.allowDrag(ev)) return; const graph = this.graph; const zoomKeys = Array.isArray(this.zoomKey) ? [].concat(this.zoomKey) : [this.zoomKey]; if (zoomKeys.includes('control')) zoomKeys.push('ctrl'); @@ -193,4 +192,17 @@ export default { this.set('timeout', timeout); } }, + allowDrag(evt: IG6GraphEvent) { + const target = evt.target; + const targetIsCanvas = target && target.isCanvas && target.isCanvas(); + if (isBoolean(this.allowDragOnItem) && !this.allowDragOnItem && !targetIsCanvas) return false; + if (isObject(this.allowDragOnItem)) { + const { node, edge, combo } = this.allowDragOnItem; + const itemType = evt.item?.getType?.(); + if (!node && itemType === 'node') return false; + if (!edge && itemType === 'edge') return false; + if (!combo && itemType === 'combo') return false; + } + return true; + } }; diff --git a/packages/pc/src/global.ts b/packages/pc/src/global.ts index 955c89e1ea..2363c634eb 100644 --- a/packages/pc/src/global.ts +++ b/packages/pc/src/global.ts @@ -7,7 +7,7 @@ const textColor = 'rgb(0, 0, 0)'; const colorSet = getColorsWithSubjectColor(subjectColor, backColor); export default { - version: '0.8.3', + version: '0.8.4', rootContainerClassName: 'root-container', nodeContainerClassName: 'node-container', edgeContainerClassName: 'edge-container', diff --git a/packages/pc/src/graph/graph.ts b/packages/pc/src/graph/graph.ts index 25909d5480..14cd2fb8e8 100644 --- a/packages/pc/src/graph/graph.ts +++ b/packages/pc/src/graph/graph.ts @@ -10,6 +10,7 @@ import Global from '../global'; import { LayoutController, EventController } from './controller'; import { PluginBase } from '@antv/g6-plugin'; import { createDom } from '@antv/dom-util'; +import { cloneGElement } from '../util/image'; const { transform } = ext; const SVG = 'svg'; @@ -376,7 +377,8 @@ export default class Graph extends AbstractGraph implements IGraph { const vCanvas = renderer === 'svg' ? new GSVGCanvas(canvasOptions) : new GCanvas(canvasOptions); const group = this.get('group'); - const vGroup = group.clone(); + // clone group and clone image shape's clip + const vGroup = cloneGElement(group); let matrix = clone(vGroup.getMatrix()); if (!matrix) matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1]; diff --git a/packages/pc/src/graph/tree-graph.ts b/packages/pc/src/graph/tree-graph.ts index 241eca52d4..65cba4be70 100644 --- a/packages/pc/src/graph/tree-graph.ts +++ b/packages/pc/src/graph/tree-graph.ts @@ -44,7 +44,7 @@ export default class TreeGraph extends Graph implements ITreeGraph { layout.type = 'dendrogram'; } if (!layout.direction) { - layout.direction = 'TB'; + layout.direction = layout.type === 'indented' ? 'LR' : 'TB'; } if (layout.radial) { return (data: any) => { diff --git a/packages/pc/src/util/image.ts b/packages/pc/src/util/image.ts new file mode 100644 index 0000000000..379ed82936 --- /dev/null +++ b/packages/pc/src/util/image.ts @@ -0,0 +1,34 @@ +import { IElement } from "@antv/g-base"; + +/** + * Clone group and clone the clip shapes of image shapes. + * @param group group to be cloned + * @returns cloned group with same clipped shapes of original group + */ +export const cloneGElement = (element: IElement): IElement => { + const vElement = element.clone(); + applyCloneClip(element, vElement); + return vElement; +} + +/** + * Apply the clipShape for image shapes from original element to cloned one (clonedElement). + * @param element original element + * @param clonedElement cloned element of original element + */ +const applyCloneClip = (element: IElement, clonedElement: IElement) => { + if (element.isGroup() && clonedElement.isGroup()) { + element.get('children')?.forEach((child, i) => { + const clonedChild = clonedElement.get('children')[i]; + applyCloneClip(child, clonedChild); + }); + } + const type = element.get('type'); + const clonedType = clonedElement.get('type'); + if (type !== 'image' || clonedType !== 'image') return; + const clipShape = element.get('clipShape'); + clonedElement.setClip({ + type: clipShape.get('type'), + attrs: clipShape.attr() + }); +} \ No newline at end of file diff --git a/packages/pc/tests/unit/behavior/create-edge-spec.ts b/packages/pc/tests/unit/behavior/create-edge-spec.ts index 835de32a31..c30cdb6fd7 100644 --- a/packages/pc/tests/unit/behavior/create-edge-spec.ts +++ b/packages/pc/tests/unit/behavior/create-edge-spec.ts @@ -292,7 +292,7 @@ describe('create-edge', () => { let stackData = graph.getStackData(); const { undoStack, redoStack } = stackData; - expect(undoStack.length).toBe(1); + expect(undoStack.length).toBe(0); expect(redoStack.length).toBe(0); // cancel @@ -304,14 +304,14 @@ describe('create-edge', () => { graph.emit('node:click', { x: 120, y: 120, item: node1 }); expect(graph.getEdges().length).toEqual(1); stackData = graph.getStackData(); - expect(stackData.undoStack.length).toBe(2); + expect(stackData.undoStack.length).toBe(1); expect(stackData.redoStack.length).toBe(0); // loop graph.emit('node:click', { x: 100, y: 100, item: node0 }); graph.emit('node:click', { x: 100, y: 100, item: node0 }); stackData = graph.getStackData(); - expect(stackData.undoStack.length).toBe(3); + expect(stackData.undoStack.length).toBe(2); expect(stackData.redoStack.length).toBe(0); expect(graph.getEdges().length).toEqual(2); const loop = graph.getEdges()[1]; diff --git a/packages/pc/tests/unit/behavior/drag-canvas-spec.ts b/packages/pc/tests/unit/behavior/drag-canvas-spec.ts index cbeff580e0..a6be0e02e9 100644 --- a/packages/pc/tests/unit/behavior/drag-canvas-spec.ts +++ b/packages/pc/tests/unit/behavior/drag-canvas-spec.ts @@ -50,7 +50,6 @@ describe('drag-canvas', () => { graph.on('canvas:dragend', () => { start = false; }); - graph.paint(); graph.emit('mousedown', { clientX: 150, clientY: 150, target: graph.get('canvas') }); graph.emit('drag', { clientX: 150, clientY: 150, target: graph.get('canvas') }); graph.emit('drag', { clientX: 200, clientY: 200, target: graph.get('canvas') }); @@ -64,7 +63,7 @@ describe('drag-canvas', () => { expect(start).toBe(false); graph.destroy(); }); - it('drag canvas with allowDragOnItem', () => { + it('drag canvas with boolean allowDragOnItem', () => { const graph = new Graph({ container: div, width: 500, @@ -87,7 +86,6 @@ describe('drag-canvas', () => { graph.on('canvas:dragend', () => { start = false; }); - graph.paint(); graph.emit('mousedown', { clientX: 150, clientY: 150, target: graph.getNodes()[0] }); graph.emit('drag', { clientX: 150, clientY: 150, target: graph.getNodes()[0] }); graph.emit('drag', { clientX: 200, clientY: 200, target: graph.getNodes()[0] }); @@ -101,6 +99,80 @@ describe('drag-canvas', () => { expect(start).toBe(false); graph.destroy(); }); + it('drag canvas with object allowDragOnItem', () => { + const graph = new Graph({ + container: div, + width: 500, + height: 500, + defaultEdge: { + size: 10 + }, + modes: { + default: [ + { + type: 'drag-canvas', + allowDragOnItem: { + node: false, + edge: true + }, + }, + ], + }, + }); + const gdata = { + ...data, + combos: [{ + id: 'combo1' + }] + }; + gdata.nodes[0].comboId = 'combo1'; + graph.data(gdata); + graph.render(); + let start = false; + graph.on('canvas:dragstart', () => { + start = true; + }); + graph.on('canvas:dragend', () => { + start = false; + }); + // drag failed on node + graph.emit('mousedown', { clientX: 150, clientY: 150, target: graph.getNodes()[0], item: graph.getNodes()[0] }); + graph.emit('drag', { clientX: 150, clientY: 150, target: graph.getNodes()[0], item: graph.getNodes()[0] }); + graph.emit('drag', { clientX: 200, clientY: 200, target: graph.getNodes()[0], item: graph.getNodes()[0] }); + expect(start).toBe(false); + graph.emit('drag', { clientX: 250, clientY: 250, target: graph.getNodes()[0], item: graph.getNodes()[0] }); + expect(start).toBe(false); + let matrix = graph.get('group').getMatrix(); + expect(matrix).toEqual(null); + graph.emit('dragend', {}); + + // drag success on edge + graph.emit('mousedown', { clientX: 150, clientY: 150, target: graph.getEdges()[0], item: graph.getEdges()[0] }); + graph.emit('drag', { clientX: 150, clientY: 150, target: graph.getEdges()[0], item: graph.getEdges()[0] }); + graph.emit('drag', { clientX: 200, clientY: 200, target: graph.getEdges()[0], item: graph.getEdges()[0] }); + expect(start).toBe(true); + graph.emit('drag', { clientX: 250, clientY: 250, target: graph.getEdges()[0], item: graph.getEdges()[0] }); + expect(start).toBe(true); + matrix = graph.get('group').getMatrix(); + expect(matrix[6]).toEqual(100); + expect(matrix[7]).toEqual(100); + graph.emit('dragend', {}); + expect(start).toBe(false); + + // drag failed on combo + graph.emit('mousedown', { clientX: 150, clientY: 150, target: graph.getCombos()[0], item: graph.getCombos()[0] }); + graph.emit('drag', { clientX: 150, clientY: 150, target: graph.getCombos()[0], item: graph.getCombos()[0] }); + graph.emit('drag', { clientX: 200, clientY: 200, target: graph.getCombos()[0], item: graph.getCombos()[0] }); + expect(start).toBe(false); + graph.emit('drag', { clientX: 250, clientY: 250, target: graph.getCombos()[0], item: graph.getCombos()[0] }); + expect(start).toBe(false); + matrix = graph.get('group').getMatrix(); + expect(matrix[6]).toEqual(100); + expect(matrix[7]).toEqual(100); + graph.emit('dragend', {}); + + graph.destroy(); + }); it('prevent default drag behavior', () => { const graph = new Graph({ container: div, @@ -126,7 +198,6 @@ describe('drag-canvas', () => { graph.on('canvas:dragend', () => { start = false; }); - graph.paint(); graph.emit('mousedown', { clientX: 150, clientY: 150, target: graph.get('canvas') }); graph.emit('drag', { clientX: 150, clientY: 150, target: graph.get('canvas') }); graph.emit('drag', { clientX: 200, clientY: 200, target: graph.get('canvas') }); @@ -197,7 +268,6 @@ describe('drag-canvas', () => { graph.on('canvas:dragend', () => { start = false; }); - graph.paint(); graph.emit('mousedown', { clientX: 150, clientY: 150, target: graph.get('canvas') }); graph.emit('drag', { clientX: 150, clientY: 150, target: graph.get('canvas') }); graph.emit('drag', { clientX: 200, clientY: 200, target: graph.get('canvas') }); @@ -227,7 +297,6 @@ describe('drag-canvas', () => { }); let start = false; graph.addItem('node', { x: 100, y: 100, color: '#666', type: 'rect', id: 'test' }); - graph.paint(); graph.emit('mousedown', { clientX: 150, clientY: 150, target: graph.get('canvas') }); graph.emit('drag', { clientX: 150, clientY: 150, target: graph.get('canvas') }); graph.emit('drag', { clientX: 250, clientY: 250, target: graph.get('canvas') }); @@ -517,7 +586,6 @@ describe('drag-canvas', () => { graph.on('canvas:dragend', () => { start = false; }); - graph.paint(); graph.emit('mousedown', { clientX: 150, clientY: 150, target: graph.get('canvas') }); graph.emit('drag', { clientX: 150, clientY: 150, target: graph.get('canvas') }); graph.emit('drag', { clientX: 200, clientY: 200, target: graph.get('canvas') }); diff --git a/packages/pc/tests/unit/element/combo-spec.ts b/packages/pc/tests/unit/element/combo-spec.ts index 08328626aa..ccfe47fa47 100644 --- a/packages/pc/tests/unit/element/combo-spec.ts +++ b/packages/pc/tests/unit/element/combo-spec.ts @@ -88,7 +88,7 @@ describe('stack', () => { graph.data(data); graph.render(); it('initial collapsed and stack', () => { - expect(graph.getUndoStack().linkedList.head.value.action).toBe('render') + expect(graph.getUndoStack().linkedList.head).toBe(null) }) it('drag combo stack', () => { graph.on('canvas:click', e => { diff --git a/packages/pc/tests/unit/element/nodes/donut-spec.ts b/packages/pc/tests/unit/element/nodes/donut-spec.ts index dffd41029c..a356bfd053 100644 --- a/packages/pc/tests/unit/element/nodes/donut-spec.ts +++ b/packages/pc/tests/unit/element/nodes/donut-spec.ts @@ -136,5 +136,6 @@ describe('donut test', () => { const fanShapes = Object.values(graph.findById('node').getContainer()['shapeMap']).filter(shape => shape.get('name').includes('fan-shape-')); expect(fanShapes[0].attr('path')[0][1]).toBe(150 / 2 * 1.6 / 2); // (keyShapeR + 0.6 * keyShapeR) / 2 expect(fanShapes[0].attr('lineWidth')).toBe(150 / 2 * (1 - 0.6)); // keyShapeR - 0.6 * keyShapeR + graph.destroy(); }); }); \ No newline at end of file diff --git a/packages/pc/tests/unit/graph/graph-spec.ts b/packages/pc/tests/unit/graph/graph-spec.ts index 9b7d949b19..a5315cfd40 100644 --- a/packages/pc/tests/unit/graph/graph-spec.ts +++ b/packages/pc/tests/unit/graph/graph-spec.ts @@ -1551,9 +1551,7 @@ describe('redo stack & undo stack', () => { let stackData = graph.getStackData(); let undoStack = stackData.undoStack; let redoStack = stackData.redoStack; - expect(undoStack.length).toBe(1); - expect(undoStack[0].action).toEqual('render'); - expect(undoStack[0].data.after.nodes.length).toEqual(2); + expect(undoStack.length).toBe(0); expect(redoStack.length).toBe(0); // update 后,undo stack 中有 2 条数据,一条 render,一条 update @@ -1564,7 +1562,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(2); + expect(undoStack.length).toBe(1); let firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('update'); @@ -1580,7 +1578,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(3); + expect(undoStack.length).toBe(2); firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('update'); @@ -1598,7 +1596,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(4); + expect(undoStack.length).toBe(3); firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('add'); @@ -1611,7 +1609,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(5); + expect(undoStack.length).toBe(4); firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('visible'); @@ -1622,7 +1620,7 @@ describe('redo stack & undo stack', () => { stackData = graph.getStackData(); undoStack = stackData.undoStack; - expect(undoStack.length).toBe(6); + expect(undoStack.length).toBe(5); firstStackData = undoStack[0]; expect(firstStackData.action).toEqual('delete'); diff --git a/packages/pc/tests/unit/layout/tree-layout-spec.ts b/packages/pc/tests/unit/layout/tree-layout-spec.ts index 1f2059f831..f205c159a7 100644 --- a/packages/pc/tests/unit/layout/tree-layout-spec.ts +++ b/packages/pc/tests/unit/layout/tree-layout-spec.ts @@ -38,7 +38,8 @@ describe('random', () => { width: 500, height: 500, fitView: true - }) + }); + G6.Util.traverseTree(layoutData, subtree => delete subtree.parent); tree.data(layoutData); tree.render(); expect(!isNaN(layoutData.x)).toBe(true); diff --git a/packages/plugin/package.json b/packages/plugin/package.json index d10ec8f094..b6b737b3b3 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-plugin", - "version": "0.8.3", + "version": "0.8.4", "description": "G6 Plugin", "main": "lib/index.js", "module": "es/index.js", @@ -22,8 +22,8 @@ "@antv/g-base": "^0.5.1", "@antv/g-canvas": "^0.5.2", "@antv/g-svg": "^0.5.2", - "@antv/g6-core": "0.8.3", - "@antv/g6-element": "0.8.3", + "@antv/g6-core": "0.8.4", + "@antv/g6-element": "0.8.4", "@antv/matrix-util": "^3.1.0-beta.3", "@antv/scale": "^0.3.4", "@antv/util": "^2.0.9", diff --git a/packages/site/docs/api/graphFunc/render.en.md b/packages/site/docs/api/graphFunc/render.en.md index abf1382a50..e7a55ddead 100644 --- a/packages/site/docs/api/graphFunc/render.en.md +++ b/packages/site/docs/api/graphFunc/render.en.md @@ -10,59 +10,3 @@ graph.render(); ``` Render the graph with data onto the canvas. - -### graph.refresh() - -Refresh the canvas when the **existing** data items' configurations is changed in the source data. - -Attention: If there are some new nodes/edges/combos to be added or some nodes/edges/combos to be removed, use [graph.addItem](/en/docs/api/graphFunc/item/#graphadditemtype-model-stack) / [graph.removeItem](/en/docs/api/graphFunc/item/#graphremoveitemitem-stack) or [graph.changeData](/en/docs/api/graphFunc/data/#graphchangedatadata-stack) instead. - -**Usage** - -```javascript -graph.refresh(); -``` - -### graph.paint() - -Repaint the canvas. Use it after changing the item's style or state. - -**Usage** - -```javascript -const item = e.item; -const graph = this.graph; - -const autoPaint = graph.get('autoPaint'); -graph.setAutoPaint(false); - -graph.setItemState(item, 'selected', true); - -graph.paint(); -graph.setAutoPaint(autoPaint); -``` - -### graph.setAutoPaint(auto) - -Whether to repaint the canvas automatically after updating or deleting items. - -**Parameters** - -| Name | Type | Required | Description | -| ---- | ------- | -------- | -------------------------------------------- | -| auto | Boolean | true | Whether to repaint the canvas automatically. | - -**Usage** - -```javascript -const item = e.item; -const graph = this.graph; - -const autoPaint = graph.get('autoPaint'); -graph.setAutoPaint(false); - -graph.setItemState(item, 'selected', true); - -graph.paint(); -graph.setAutoPaint(autoPaint); -``` diff --git a/packages/site/docs/api/graphFunc/render.zh.md b/packages/site/docs/api/graphFunc/render.zh.md index 1480fa5102..1ce35beeb0 100644 --- a/packages/site/docs/api/graphFunc/render.zh.md +++ b/packages/site/docs/api/graphFunc/render.zh.md @@ -12,63 +12,3 @@ order: 1 ```javascript graph.render(); ``` - -### graph.refresh() - -当源数据中**现有**节点/边/ Combo 的数据项发生配置的变更时,根据新数据刷新视图。 - -注意:节点/边/ Combo 数据的增删需要使用 [graph.addItem](/zh/docs/api/graphFunc/item/#graphadditemtype-model-stack) / [graph.removeItem](/zh/docs/api/graphFunc/item/#graphremoveitemitem-stack) 或 [graph.changeData](/zh/docs/api/graphFunc/data/#graphchangedatadata-stack)。 - -该方法无参数。 - -**用法** - -```javascript -graph.refresh(); -``` - -### graph.paint() - -仅重新绘制画布。当设置了元素样式或状态后,通过调用 `paint()` 方法,让修改生效。 - -该方法无参数。 - -**用法** - -```javascript -const item = e.item; -const graph = this.graph; - -const autoPaint = graph.get('autoPaint'); -graph.setAutoPaint(false); - -graph.setItemState(item, 'selected', true); - -graph.paint(); -graph.setAutoPaint(autoPaint); -``` - -### graph.setAutoPaint(auto) - -设置是否在更新/删除后自动重绘,一般搭配 `paint()` 方法使用。 - -**参数** - -| 名称 | 类型 | 是否必选 | 描述 | -| ---- | ------- | -------- | ------------ | -| auto | Boolean | true | 是否自动重绘 | - -**用法** - -```javascript -const item = e.item; -const graph = this.graph; - -const autoPaint = graph.get('autoPaint'); -graph.setAutoPaint(false); - -graph.setItemState(item, 'selected', true); - -graph.paint(); -graph.setAutoPaint(autoPaint); -``` diff --git a/packages/site/docs/api/graphLayout/concentric.en.md b/packages/site/docs/api/graphLayout/concentric.en.md index 2ae520d8d9..63bafc7d5c 100644 --- a/packages/site/docs/api/graphLayout/concentric.en.md +++ b/packages/site/docs/api/graphLayout/concentric.en.md @@ -41,7 +41,7 @@ const graph = new G6.Graph({ **Type**: Number
**Default**: 30
**Required**: false
**Description**: The diameter of the node. It is used for preventing node overlappings -## layoutCfg.minNodeSpacing +## layoutCfg.nodeSpacing **Type**: Number
**Default**: 10
**Required**: false
**Description**: The minimum separation between adjacent circles diff --git a/packages/site/docs/api/graphLayout/concentric.zh.md b/packages/site/docs/api/graphLayout/concentric.zh.md index 742817362f..594e56fad3 100644 --- a/packages/site/docs/api/graphLayout/concentric.zh.md +++ b/packages/site/docs/api/graphLayout/concentric.zh.md @@ -41,7 +41,7 @@ const graph = new G6.Graph({ **类型**: Number
**默认值**:30
**是否必须**:false
**说明**:节点大小(直径)。用于防止节点重叠时的碰撞检测 -## layoutCfg.minNodeSpacing +## layoutCfg.nodeSpacing **类型**: Number
**默认值**:10
**是否必须**:false
**说明**:环与环之间最小间距,用于调整半径 diff --git a/packages/site/docs/api/graphLayout/force2.en.md b/packages/site/docs/api/graphLayout/force2.en.md index fe795df618..bae7db176a 100644 --- a/packages/site/docs/api/graphLayout/force2.en.md +++ b/packages/site/docs/api/graphLayout/force2.en.md @@ -41,6 +41,22 @@ If you want to fix the positions for some nodes during calculation, assign `fx` **Type**: Boolean
**示例**:false
**Default**: :false
**Default**: false
**Description**: Whether refresh the node positions on the canvas each iteration. If it is `true`, the nodes on the canvas will looks like animating with forces +## layoutCfg.preset + +_Supported from v4.7.0_ + +**Type**: + +```javascript +{ + type: string; // preset layout name, could be any static layout like random, concentric, grid, circular, radial, and dagre + [key: string]: unkown; // corresponding configurations for the preset layout type +} +``` +
+ +**Default**: undefined
**Required**: false
**Description**: Preset layout calculates intialize positions for nodes, and the force layout will start from the inited result. The quality of the force layout's result depends on the initial positions of nodes. Configuring a proper preset for a force layout will speed up the convergence of force layout, and enhance the quality in the same time. By default, the positions of nodes will be inited as grid + ## layoutCfg.linkDistance **Type**: Number / Function
**Default**: 1
**Required**: false
**Description**: The edge length diff --git a/packages/site/docs/api/graphLayout/force2.zh.md b/packages/site/docs/api/graphLayout/force2.zh.md index cb620aa14f..5e2e86cf47 100644 --- a/packages/site/docs/api/graphLayout/force2.zh.md +++ b/packages/site/docs/api/graphLayout/force2.zh.md @@ -41,6 +41,22 @@ const graph = new G6.Graph({ **类型**: Boolean
**示例**:false
**默认值**::false
**是否必须**:false
**说明**:是否每次迭代都刷新画布,若为 `true` 则将表现出带有动画逐步布局的效果 +## layoutCfg.preset + +_自 G6 v4.7.0 起支持。_ + +**类型**: + +```javascript +{ + type: string; // 布局名称,可以是 random、concentric、grid、circular、radial、dagre 等静态布局 + [key: string]: unkown; // 对应布局的配置项 +} +``` +
+ +**默认值**: undefined
**是否必须**: false
**说明**: 力导向布局的初始化布局,将先执行 preset 指定的布局,再进行力导向计算。由于力导向布局的结果非常依赖节点的初始位置,配置 preset 的可以给力导向布局一个好的初始化,让力导向算法更快收敛、效果更好。默认情况下,力导向的初始化为格子布局(grid)的结果 + ## layoutCfg.linkDistance **类型**: Number / Function
**默认值**1
**是否必须**:false
**说明**:边长度 diff --git a/packages/site/docs/api/graphLayout/forceAtlas2.en.md b/packages/site/docs/api/graphLayout/forceAtlas2.en.md index 19a006f20a..2f5e69b3e6 100644 --- a/packages/site/docs/api/graphLayout/forceAtlas2.en.md +++ b/packages/site/docs/api/graphLayout/forceAtlas2.en.md @@ -37,6 +37,23 @@ const graph = new G6.Graph({ **Type**: Boolean
**Default**: false
**Required**: false
**Description**: Whether to enable the web-worker in case layout calculation takes too long to block page interaction. ⚠️ Notice: When `workerEnabled: true`, all the function type parameters are not supported. + +## layoutCfg.preset + +_Supported from v4.7.0_ + +**Type**: + +```javascript +{ + type: string; // preset layout name, could be any static layout like random, concentric, grid, circular, radial, and dagre + [key: string]: unkown; // corresponding configurations for the preset layout type +} +``` +
+ +**Default**: undefined
**Required**: false
**Description**: Preset layout calculates intialize positions for nodes, and the force layout will start from the inited result. The quality of the force layout's result depends on the initial positions of nodes. Configuring a proper preset for a force layout will speed up the convergence of force layout, and enhance the quality in the same time. By default, the positions of nodes will be inited as grid + ## layoutCfg.kr **Type**: Number
**Default**: 5
**Required**: false
**Description**: Repulsive parameter, smaller the kr, more compact the graph diff --git a/packages/site/docs/api/graphLayout/forceAtlas2.zh.md b/packages/site/docs/api/graphLayout/forceAtlas2.zh.md index a784d003c3..8e4d97dd98 100644 --- a/packages/site/docs/api/graphLayout/forceAtlas2.zh.md +++ b/packages/site/docs/api/graphLayout/forceAtlas2.zh.md @@ -37,6 +37,22 @@ const graph = new G6.Graph({ **类型**: Boolean
**默认值**: false
**是否必须**: false
**说明**: 是否启用 web-worker 以防布局计算时间过长阻塞页面交互。 ⚠️ 注意: `workerEnabled: true` 时,不支持所有函数类型的参数。 +## layoutCfg.preset + +_自 G6 v4.7.0 起支持。_ + +**类型**: + +```javascript +{ + type: string; // 布局名称,可以是 random、concentric、grid、circular、radial、dagre 等静态布局 + [key: string]: unkown; // 对应布局的配置项 +} +``` +
+ +**默认值**: undefined
**是否必须**: false
**说明**: 力导向布局的初始化布局,将先执行 preset 指定的布局,再进行力导向计算。由于力导向布局的结果非常依赖节点的初始位置,配置 preset 的可以给力导向布局一个好的初始化,让力导向算法更快收敛、效果更好。默认情况下,力导向的初始化为格子布局(grid)的结果 + ## layoutCfg.kr **类型**: Number
**默认值**:5
**是否必须**:false
**说明**:斥力系数,可用于调整布局的紧凑程度。kr 越大,布局越松散 diff --git a/packages/site/docs/api/graphLayout/fruchterman.en.md b/packages/site/docs/api/graphLayout/fruchterman.en.md index 4d1f2acb68..4a0fcbd31b 100644 --- a/packages/site/docs/api/graphLayout/fruchterman.en.md +++ b/packages/site/docs/api/graphLayout/fruchterman.en.md @@ -32,6 +32,24 @@ If you want to fix the positions for some nodes during calculation, assign `fx` **Type**: Array
**Example**: [ 0, 0 ]
**Default**: The center of the graph
**Required**: false
**Description**: The center of the layout + +## layoutCfg.preset + +_Supported from v4.7.0_ + +**Type**: + +```javascript +{ + type: string; // preset layout name, could be any static layout like random, concentric, grid, circular, radial, and dagre + [key: string]: unkown; // corresponding configurations for the preset layout type +} +``` +
+ +**Default**: undefined
**Required**: false
**Description**: Preset layout calculates intialize positions for nodes, and the force layout will start from the inited result. The quality of the force layout's result depends on the initial positions of nodes. Configuring a proper preset for a force layout will speed up the convergence of force layout, and enhance the quality in the same time. By default, the positions of nodes will be inited as grid + + ## layoutCfg.maxIteration **Type**: Number
**Default**: 1000
**Required**: false
**Description**: The maximum iteration number diff --git a/packages/site/docs/api/graphLayout/fruchterman.zh.md b/packages/site/docs/api/graphLayout/fruchterman.zh.md index c0e4d84c05..a2b02629d2 100644 --- a/packages/site/docs/api/graphLayout/fruchterman.zh.md +++ b/packages/site/docs/api/graphLayout/fruchterman.zh.md @@ -32,6 +32,22 @@ const graph = new G6.Graph({ **类型**: Array
**示例**:[ 0, 0 ]
**默认值**:图的中心
**是否必须**:false
**说明**:布局的中心 +## layoutCfg.preset + +_自 G6 v4.7.0 起支持。_ + +**类型**: + +```javascript +{ + type: string; // 布局名称,可以是 random、concentric、grid、circular、radial、dagre 等静态布局 + [key: string]: unkown; // 对应布局的配置项 +} +``` +
+ +**默认值**: undefined
**是否必须**: false
**说明**: 力导向布局的初始化布局,将先执行 preset 指定的布局,再进行力导向计算。由于力导向布局的结果非常依赖节点的初始位置,配置 preset 的可以给力导向布局一个好的初始化,让力导向算法更快收敛、效果更好。默认情况下,力导向的初始化为格子布局(grid)的结果 + ## layoutCfg.maxIteration **类型**: Number
**默认值**:1000
**是否必须**:false
**说明**:最大迭代次数 diff --git a/packages/site/docs/api/graphLayout/gforce.en.md b/packages/site/docs/api/graphLayout/gforce.en.md index 6d98d218f0..5858debb07 100644 --- a/packages/site/docs/api/graphLayout/gforce.en.md +++ b/packages/site/docs/api/graphLayout/gforce.en.md @@ -38,6 +38,23 @@ If you want to fix the positions for some nodes during calculation, assign `fx` **Type**: Array
**Example**: [ 0, 0 ]
**Default**: The center of the graph
**Required**: false
**Description**: The center of the layout +## layoutCfg.preset + +_Supported from v4.7.0_ + +**Type**: + +```javascript +{ + type: string; // preset layout name, could be any static layout like random, concentric, grid, circular, radial, and dagre + [key: string]: unkown; // corresponding configurations for the preset layout type +} +``` +
+ +**Default**: undefined
**Required**: false
**Description**: Preset layout calculates intialize positions for nodes, and the force layout will start from the inited result. The quality of the force layout's result depends on the initial positions of nodes. Configuring a proper preset for a force layout will speed up the convergence of force layout, and enhance the quality in the same time. By default, the positions of nodes will be inited as grid + + ## layoutCfg.linkDistance **Type**: Number / Function
**Default**: 1
**Required**: false
**Description**: The edge length diff --git a/packages/site/docs/api/graphLayout/gforce.zh.md b/packages/site/docs/api/graphLayout/gforce.zh.md index e7e1433df1..ea0a1699be 100644 --- a/packages/site/docs/api/graphLayout/gforce.zh.md +++ b/packages/site/docs/api/graphLayout/gforce.zh.md @@ -38,6 +38,22 @@ const graph = new G6.Graph({ **类型**: Array
**示例**:[ 0, 0 ]
**默认值**:图的中心
**是否必须**:false
**说明**:布局的中心 +## layoutCfg.preset + +_自 G6 v4.7.0 起支持。_ + +**类型**: + +```javascript +{ + type: string; // 布局名称,可以是 random、concentric、grid、circular、radial、dagre 等静态布局 + [key: string]: unkown; // 对应布局的配置项 +} +``` +
+ +**默认值**: undefined
**是否必须**: false
**说明**: 力导向布局的初始化布局,将先执行 preset 指定的布局,再进行力导向计算。由于力导向布局的结果非常依赖节点的初始位置,配置 preset 的可以给力导向布局一个好的初始化,让力导向算法更快收敛、效果更好。默认情况下,力导向的初始化为格子布局(grid)的结果 + ## layoutCfg.linkDistance **类型**: Number / Function
**默认值**1
**是否必须**:false
**说明**:边长度 diff --git a/packages/site/docs/api/graphLayout/guide.en.md b/packages/site/docs/api/graphLayout/guide.en.md index 72a7a47d45..89b6b627bb 100644 --- a/packages/site/docs/api/graphLayout/guide.en.md +++ b/packages/site/docs/api/graphLayout/guide.en.md @@ -46,6 +46,9 @@ The configurations of each layout algorithms are different. Please refer to corr - If there are `x` and `y` in node data, the graph will render with these information; - If there is no positions information in node data, the graph will arrange nodes with Random Layout by default. +If the worker is enabled, notice that worker will visit the latest online version of @antv/layout. If your application cannot reach the online resource, download and save the [layout script](https://unpkg.com/@antv/layout@latest/dist/layout.min.js), and put it on an address which is visitable for your application. And then config `workerScriptURL` with the address in `layout`. + + ## Instantiate Independently The functions in this section should be concerned in these two situation: diff --git a/packages/site/docs/api/graphLayout/guide.zh.md b/packages/site/docs/api/graphLayout/guide.zh.md index 25e1337bee..0271a79e20 100644 --- a/packages/site/docs/api/graphLayout/guide.zh.md +++ b/packages/site/docs/api/graphLayout/guide.zh.md @@ -49,6 +49,8 @@ const graph = new G6.Graph({ - 若数据中节点有位置信息(`x` 和 `y`),则按照数据的位置信息进行绘制; - 若数据中节点没有位置信息,则默认使用 Random Layout 进行布局。 +如果开启了 webworker,worker 使用的是 @antv/layout 线上的脚本,如果你的项目无法访问到线上资源,请保存 [layout 脚本](https://unpkg.com/@antv/layout@latest/dist/layout.min.js),并放在可以访问到的地址上,将 layout 的 `workerScriptURL` 配置为该地址即可。 + ## 单独使用布局 以下方法为通过 `const layout = new G6.Layout['layoutName']` 单独使用布局时,或自定义布局时可能需要复写的方法。如果上述两种情况,仅在实例化图时通过配置 `layout` 使用内置布局方法时,以下方法由 G6 控制并调用,用户不需要了解。 diff --git a/packages/site/docs/manual/middle/elements/methods/edgeVisible.en.md b/packages/site/docs/manual/middle/elements/methods/edgeVisible.en.md index b61d4c449f..d9f4e2283a 100644 --- a/packages/site/docs/manual/middle/elements/methods/edgeVisible.en.md +++ b/packages/site/docs/manual/middle/elements/methods/edgeVisible.en.md @@ -39,7 +39,6 @@ graph.on('node:click', (ev) => { const node = ev.item; console.log('before hide(), the nodevisible = ', node.get('visible')); node.hide(); - graph.paint(); console.log('after hide(), the node visible = ', node.get('visible')); }); @@ -48,7 +47,6 @@ graph.on('edge:click', (ev) => { const edge = ev.item; console.log('before hide(), the edge visible = ', edge.get('visible')); edge.hide(); - graph.paint(); console.log('after hide(), the edge visible = ', edge.get('visible')); }); @@ -62,6 +60,5 @@ graph.on('canvas:click', (ev) => { edges.forEach((edge) => { edge.show(); }); - graph.paint(); }); ``` diff --git a/packages/site/docs/manual/middle/elements/methods/edgeVisible.zh.md b/packages/site/docs/manual/middle/elements/methods/edgeVisible.zh.md index 092d264fcd..67f7ba1517 100644 --- a/packages/site/docs/manual/middle/elements/methods/edgeVisible.zh.md +++ b/packages/site/docs/manual/middle/elements/methods/edgeVisible.zh.md @@ -39,7 +39,6 @@ graph.on('node:click', (ev) => { const node = ev.item; console.log('before hide(), the nodevisible = ', node.get('visible')); node.hide(); - graph.paint(); console.log('after hide(), the node visible = ', node.get('visible')); }); @@ -48,7 +47,6 @@ graph.on('edge:click', (ev) => { const edge = ev.item; console.log('before hide(), the edge visible = ', edge.get('visible')); edge.hide(); - graph.paint(); console.log('after hide(), the edge visible = ', edge.get('visible')); }); @@ -62,6 +60,5 @@ graph.on('canvas:click', (ev) => { edges.forEach((edge) => { edge.show(); }); - graph.paint(); }); ``` diff --git a/packages/site/docs/manual/middle/elements/methods/elementIndex.en.md b/packages/site/docs/manual/middle/elements/methods/elementIndex.en.md index a6ddbedfe0..c5aa5091a1 100644 --- a/packages/site/docs/manual/middle/elements/methods/elementIndex.en.md +++ b/packages/site/docs/manual/middle/elements/methods/elementIndex.en.md @@ -141,8 +141,6 @@ const nodes = graph.getNodes(); nodes.forEach((node) => { node.toFront(); }); -// Repaint the graph after shifting -graph.paint(); ```
Now, all the nodes are drawed on the top of edges:
img @@ -192,8 +190,6 @@ graph.on('edge:mouseenter', (ev) => { edge.toFront(); source.toFront(); target.toFront(); - // Attention: the following code must be called to repaint the graph - graph.paint(); }); graph.on('edge:mouseleave', (ev) => { @@ -203,8 +199,6 @@ graph.on('edge:mouseleave', (ev) => { edges.forEach((edge) => { edge.toBack(); }); - // Attention: the following code must be called to repaint the graph - graph.paint(); }); graph.on('node:mouseenter', (ev) => { @@ -218,8 +212,6 @@ graph.on('node:mouseenter', (ev) => { edge.getSource().toFront(); edge.getTarget().toFront(); }); - // Attention: the following code must be called to repaint the graph - graph.paint(); }); graph.on('node:mouseleave', (ev) => { @@ -229,7 +221,5 @@ graph.on('node:mouseleave', (ev) => { edges.forEach((edge) => { edge.toBack(); }); - // Attention: the following code must be called to repaint the graph - graph.paint(); }); ``` diff --git a/packages/site/docs/manual/middle/elements/methods/elementIndex.zh.md b/packages/site/docs/manual/middle/elements/methods/elementIndex.zh.md index 93ae552694..f165cf390b 100644 --- a/packages/site/docs/manual/middle/elements/methods/elementIndex.zh.md +++ b/packages/site/docs/manual/middle/elements/methods/elementIndex.zh.md @@ -139,8 +139,6 @@ const nodes = graph.getNodes(); nodes.forEach((node) => { node.toFront(); }); -// 更改层级后需要重新绘制图 -graph.paint(); ```
这样,所有节点被绘制在边上层:
img @@ -190,8 +188,6 @@ graph.on('edge:mouseenter', (ev) => { edge.toFront(); source.toFront(); target.toFront(); - // 注意:必须调用以根据新的层级顺序重绘 - graph.paint(); }); graph.on('edge:mouseleave', (ev) => { @@ -201,8 +197,6 @@ graph.on('edge:mouseleave', (ev) => { edges.forEach((edge) => { edge.toBack(); }); - // 注意:必须调用以根据新的层级顺序重绘 - graph.paint(); }); graph.on('node:mouseenter', (ev) => { @@ -216,8 +210,6 @@ graph.on('node:mouseenter', (ev) => { edge.getSource().toFront(); edge.getTarget().toFront(); }); - // 注意:必须调用以根据新的层级顺序重绘 - graph.paint(); }); graph.on('node:mouseleave', (ev) => { @@ -227,7 +219,5 @@ graph.on('node:mouseleave', (ev) => { edges.forEach((edge) => { edge.toBack(); }); - // 注意:必须调用以根据新的层级顺序重绘 - graph.paint(); }); ``` diff --git a/packages/site/docs/manual/middle/elements/methods/lock-node.en.md b/packages/site/docs/manual/middle/elements/methods/lock-node.en.md index 20358ed517..0e24afee67 100644 --- a/packages/site/docs/manual/middle/elements/methods/lock-node.en.md +++ b/packages/site/docs/manual/middle/elements/methods/lock-node.en.md @@ -57,7 +57,6 @@ G6.registerBehavior('drag-canvas-exclude-lockedNode', { lockedNodes.forEach((node) => { node.get('group').translate(dx, dy); }); - this.graph.paint(); }, onMouseDown(e) { if (this.keydown) { @@ -208,7 +207,6 @@ G6.registerBehavior('zoom-canvas-exclude-lockedNode', { ]); node.get('group').setMatrix(matrix); }); - graph.paint(); graph.emit('wheelzoom', e); }, }); diff --git a/packages/site/docs/manual/middle/elements/methods/lock-node.zh.md b/packages/site/docs/manual/middle/elements/methods/lock-node.zh.md index 3f190d3c9a..8e51850c80 100644 --- a/packages/site/docs/manual/middle/elements/methods/lock-node.zh.md +++ b/packages/site/docs/manual/middle/elements/methods/lock-node.zh.md @@ -57,7 +57,6 @@ G6.registerBehavior('drag-canvas-exclude-lockedNode', { lockedNodes.forEach((node) => { node.get('group').translate(dx, dy); }); - this.graph.paint(); }, onMouseDown(e) { if (this.keydown) { @@ -208,7 +207,6 @@ G6.registerBehavior('zoom-canvas-exclude-lockedNode', { ]); node.get('group').setMatrix(matrix); }); - graph.paint(); graph.emit('wheelzoom', e); }, }); diff --git a/packages/site/docs/manual/middle/layout/graph-layout.en.md b/packages/site/docs/manual/middle/layout/graph-layout.en.md index d5af4c63c7..4787aaab5f 100644 --- a/packages/site/docs/manual/middle/layout/graph-layout.en.md +++ b/packages/site/docs/manual/middle/layout/graph-layout.en.md @@ -295,7 +295,7 @@ centripetalOptions: { | --- | --- | --- | --- | --- | | center | Array | [ 0, 0 ] | The center of the graph | The center of the layout | | nodeSize | Number | 30 | 30 | The diameter of the node. It is used for preventing node overlappings | -| minNodeSpacing | Number | 10 | 10 | The minimum separation between adjacent circles | +| nodeSpacing | Number | 10 | 10 | The minimum separation between adjacent circles | | preventOverlap | Boolean | false | false | Whether to prevent node overlappings. To activate preventing node overlappings, `nodeSize` is required, which is used for collide detection. The size in the node data will take effect if `nodeSize` is not assigned. If the size in node data does not exist either, `nodeSize` is assigned to 30 by default | | sweep | Number | Math.PI | undefined | How many radians should be between the first and last node (defaults to full circle). If it is undefined, 2 _ Math.PI _ (1 - 1 / | level.nodes | ) will be used, where level.nodes is nodes set of each level, | level.nodes | is the number of nodes of the level | | equidistant | Boolean | false | false | Whether levels have an equal radial distance between them, may cause bounding box overflow | diff --git a/packages/site/docs/manual/middle/layout/graph-layout.zh.md b/packages/site/docs/manual/middle/layout/graph-layout.zh.md index 0b70139a40..42abfd0cff 100644 --- a/packages/site/docs/manual/middle/layout/graph-layout.zh.md +++ b/packages/site/docs/manual/middle/layout/graph-layout.zh.md @@ -296,7 +296,7 @@ centripetalOptions: { | --- | --- | --- | --- | --- | | center | Array | [ 0, 0 ] | 图的中心 | 布局的中心 | | nodeSize | Number | 30 | 30 | 节点大小(直径)。用于防止节点重叠时的碰撞检测 | -| minNodeSpacing | Number | 10 | 10 | 环与环之间最小间距,用于调整半径 | +| nodeSpacing | Number | 10 | 10 | 环与环之间最小间距,用于调整半径 | | preventOverlap | Boolean | false | false | 是否防止重叠,必须配合属性 `nodeSize` ,只有设置了与当前图节点大小相同的 `nodeSize` 值,才能够进行节点重叠的碰撞检测。若未设置 `nodeSize` ,则将根据节点数据中的 `size` 进行碰撞检测。若二者都未设置,则默认以 30 为节点大小进行碰撞检测 | | sweep | Number | Math.PI | undefined | 第一个节点与最后一个节点之间的弧度差 | | equidistant | Boolean | false | false | 环与环之间的距离是否相等 | diff --git a/packages/site/docs/manual/middle/layout/tree-graph-layout.en.md b/packages/site/docs/manual/middle/layout/tree-graph-layout.en.md index 9f9d25cd74..f19db5621a 100644 --- a/packages/site/docs/manual/middle/layout/tree-graph-layout.en.md +++ b/packages/site/docs/manual/middle/layout/tree-graph-layout.en.md @@ -86,6 +86,7 @@ const graph = new G6.TreeGraph({ | getHeight | Function | (d) => {
  // d is a node
  return 10;
} | undefined | The height of each node | | getWidth | Function | (d) => {
  // d is a node
  return 20;
} | undefined | The width of each node | | getSide | Function | (d) => {
  // d is a node
  return 'left';
} | undefined | The callback function of node position(left or right of root node). Only affects the nodes which are connected to the root node directly. And the descendant nodes will be placed according to it | +| align | 'center' / undefined | 'center' | undefined | Tell the layout whether the nodes drawing aligned at the center or the left-top. Built-in nodes are all aligned at the center, e.g. built-in 'circle' type node has circle type keyShape, and the circle shape's x and y are assigned with 0, which means the origin of this node's coordinate system is aligned at the circle's center; built-in 'rect' type node has rect type keyShape, and the rect shape's x and y are assigned with `width / 2` and `height / 2` respectively, which means the origin of this node's coordinate system is aligned at the center of the rect shape. But user customed node type with rect keyShape usually has [0, 0] for x and y, which means the origin is aligned at the left-top of the rect. Tell layout this info and correct `getWidth`, `getHeight` in the same time will make the coordinate calculation more precise. | ### mindmap diff --git a/packages/site/docs/manual/middle/layout/tree-graph-layout.zh.md b/packages/site/docs/manual/middle/layout/tree-graph-layout.zh.md index fae1c9d82f..f2c8b89d64 100644 --- a/packages/site/docs/manual/middle/layout/tree-graph-layout.zh.md +++ b/packages/site/docs/manual/middle/layout/tree-graph-layout.zh.md @@ -86,6 +86,7 @@ const graph = new G6.TreeGraph({ | getHeight | Function | (d) => {
  // d 是一个节点
  return 10;
} | undefined | 节点高度的回调函数 | | getWidth | Function | (d) => {
  // d 是一个节点
  return 20;
} | undefined | 节点宽度的回调函数 | | getSide | Function | (d) => {
  // d 是一个节点
  return 'left';
} | undefined | 节点放置在根节点左侧或右侧的回调函数,仅对与根节点直接相连的节点有效,设置后将会影响被设置节点的所有子孙节点 | +| align | 'center' / undefined | 'center' | undefined | 告知 indented 布局,节点在绘制时自身坐标系的原点在其中心还是左上角。所有的内置节点的自身坐标系原点均在中心,例如 'circle' 类型节点的 keyShape 是圆形图形,它的圆心 x、y 被设置为 0,代表原点对齐在该 keyShape 的圆心(即中心);而 'rect' 类型的内置节点 keyShape 是矩形图形,内置定义中它的 x、y 分别被设置为 `width / 2` 和 `height / 2`,意味着矩形的左上角在 [width / 2, height / 2] 坐标上,那么坐标系原点就在矩形的中心。但自定义的 keyShape 为矩形的节点中,矩形的 x、y 可能被设置为 0,那么这一节点类型的自身坐标系原点就在其左上角。从整个图来看,每个节点的自身坐标系原点,将对齐到布局计算的节点位置上(画布绘制坐标系)。根据你使用的节点类型,告知 indented 布局是如何对齐的,可以更准确地计算节点位置,当然,这同时需要配合准确的 `getWidth` 和 `getHeight` 的返回值 | ### mindmap diff --git a/packages/site/docs/manual/middle/layout/webworker.en.md b/packages/site/docs/manual/middle/layout/webworker.en.md index 8223636803..dabafa8535 100644 --- a/packages/site/docs/manual/middle/layout/webworker.en.md +++ b/packages/site/docs/manual/middle/layout/webworker.en.md @@ -19,4 +19,5 @@ const graph = new G6.Graph({ Note: - TreeGraph layouts do not support Web-Worker; -- Sub-Graph layout mechanism do not support Web-Worker. +- Sub-Graph layout mechanism do not support Web-Worker; +- Worker will visit the latest online version of @antv/layout. If your application cannot reach the online resource, download and save the [layout script](https://unpkg.com/@antv/layout@latest/dist/layout.min.js), and put it on an address which is visitable for your application. And then config `workerScriptURL` with the address in `layout`. diff --git a/packages/site/docs/manual/middle/layout/webworker.zh.md b/packages/site/docs/manual/middle/layout/webworker.zh.md index c9af72e654..bfbdd3f1c6 100644 --- a/packages/site/docs/manual/middle/layout/webworker.zh.md +++ b/packages/site/docs/manual/middle/layout/webworker.zh.md @@ -20,4 +20,5 @@ const graph = new G6.Graph({ 注意: - 树图不支持 Web-Worker 机制; -- 子图布局机制暂不支持 Web-Worker 机制。 +- 子图布局机制暂不支持 Web-Worker 机制; +- worker 使用的是 @antv/layout 线上的脚本,如果你的项目无法访问到线上资源,请保存 [layout 脚本](https://unpkg.com/@antv/layout@latest/dist/layout.min.js),并放在可以访问到的地址上,将 layout 的 `workerScriptURL` 配置为该地址即可 diff --git a/packages/site/docs/manual/middle/states/defaultBehavior.en.md b/packages/site/docs/manual/middle/states/defaultBehavior.en.md index 1e661801d0..d72ec23171 100644 --- a/packages/site/docs/manual/middle/states/defaultBehavior.en.md +++ b/packages/site/docs/manual/middle/states/defaultBehavior.en.md @@ -117,7 +117,7 @@ const graph = new G6.Graph({ - - `allowDragOnItem`: whether response when the users drag on items(node/edge/combo), `false` by default; + - `allowDragOnItem`: whether response when the users drag on items(node/edge/combo), `false` by default. **Supported by 4.8.4** Allow object config with type `{ node?: boolean, edge?: boolean, combo?: boolean }` to control draggable on different item types; - Related timing events: - `canvas:dragstart`: Triggered when drag start. Listened by `graph.on('canvas:dragstart', e => {...})`; - `canvas:drag`: Triggered when dragging. Listened by `graph.on('canvas:drag', e => {...})`; @@ -162,7 +162,7 @@ The canvas can be dragged along x direction only.
diff --git a/packages/site/docs/manual/middle/states/defaultBehavior.zh.md b/packages/site/docs/manual/middle/states/defaultBehavior.zh.md index 78c3dbc5da..81eb3a63d1 100644 --- a/packages/site/docs/manual/middle/states/defaultBehavior.zh.md +++ b/packages/site/docs/manual/middle/states/defaultBehavior.zh.md @@ -114,7 +114,7 @@ const graph = new G6.Graph({ - `direction`:允许拖拽方向,支持`'x'`,`'y'`,`'both'`,默认方向为 `'both'`; - `enableOptimize`:是否开启优化,开启后拖动画布过程中隐藏所有的边及节点上非 keyShape 部分,默认关闭; - `shouldBegin(e, self)`:是否允许触发该操作。**v4.7.16 起支持** 最后一个参数为 behavior 实例,方便在箭头函数定义的 `shouldBegin` 中访问该实例; - - `allowDragOnItem`:是否允许用户在节点/边/ combo 上拖拽时响应,默认为 false; + - `allowDragOnItem`:是否允许用户在节点/边/ combo 上拖拽时响应,默认为 false。**v4.8.4 起支持:** 支持配置类型为 `{ node?: boolean, edge?: boolean, combo?: boolean }` 的对象,以支持控制是否允许响应不同元素类型上的拖拽事件; - `scalableRange`:拖动 canvas 可扩展的范围,默认为 0,值为 -1 ~ 1 代表可超出视口的范围的比例值(相对于视口大小)。值小于 -1 或大于 1 时,为正和负数时的效果如下图所示。 @@ -164,7 +164,7 @@ const graph = new G6.Graph({ - `enableOptimize`:是否开启优化,开启后拖动画布过程中隐藏所有的边及节点上非 keyShape 部分,默认关闭; - `zoomKey`:切换为滚动缩放的键盘按钮,按住该键并滚动滚轮,则切换为滚轮缩放画布,可选项为:`'shift'`,`'ctrl'`,`'alt'`,`'control'`,`'meta'`, 可使用数组监听多个按键,任意按键按下时都会触发缩放; - `scalableRange`:拖动 canvas 可扩展的范围,默认为 0,值为 -1 ~ 1 代表可超出视口的范围的比例值(相对于视口大小)。值小于 -1 或大于 1 时,为正和负数时的效果如下图所示。 - - `allowDragOnItem`:是否允许用户在节点/边/ combo 上拖拽时响应,默认为 false; + - `allowDragOnItem`:是否允许用户在节点/边/ combo 上拖拽时响应,默认为 false。**v4.8.4 起支持:** 支持配置类型为 `{ node?: boolean, edge?: boolean, combo?: boolean }` 的对象,以支持控制是否允许响应不同元素类型上的拖拽事件; diff --git a/packages/site/examples/algorithm/algoDemos/demo/shortestPath.js b/packages/site/examples/algorithm/algoDemos/demo/shortestPath.js index f44ff58fb5..dfdac0f64a 100644 --- a/packages/site/examples/algorithm/algoDemos/demo/shortestPath.js +++ b/packages/site/examples/algorithm/algoDemos/demo/shortestPath.js @@ -41,9 +41,24 @@ fetch('https://gw.alipayobjects.com/os/bmw-prod/b0ca4b15-bd0c-43ec-ae41-c810374a graph.data(data); graph.render(); + // store the selected nodes according to the clicked order + let selectedNodeIds = []; + graph.on('node:click', ({ item }) => { + const id = item.getID(); + const index = selectedNodeIds.indexOf(id); + if (item.hasState('selected') && index < 0) { + selectedNodeIds.push(id); + } else if (!item.hasState('selected')) { + selectedNodeIds.splice(index, 1); + } + }); + + graph.on('canvas:click', e => { + selectedNodeIds = []; + }); + button.addEventListener('click', (e) => { - const selectedNodes = graph.findAllByState('node', 'selected'); - if (selectedNodes.length !== 2) { + if (selectedNodeIds.length !== 2) { alert('Please select TWO nodes!\n\r请选择有且两个节点!'); return; } @@ -52,35 +67,39 @@ fetch('https://gw.alipayobjects.com/os/bmw-prod/b0ca4b15-bd0c-43ec-ae41-c810374a // path 为其中一条最短路径,allPath 为所有的最短路径 const { path, allPath } = findShortestPath( data, - selectedNodes[0].getID(), - selectedNodes[1].getID(), + selectedNodeIds[0], + selectedNodeIds[1], + true ); + selectedNodeIds = []; - const pathNodeMap = {}; - path.forEach((id) => { - const pathNode = graph.findById(id); - pathNode.toFront(); - graph.setItemState(pathNode, 'highlight', true); - pathNodeMap[id] = true; - }); - graph.getEdges().forEach((edge) => { - const edgeModel = edge.getModel(); - const source = edgeModel.source; - const target = edgeModel.target; - const sourceInPathIdx = path.indexOf(source); - const targetInPathIdx = path.indexOf(target); - if (sourceInPathIdx === -1 || targetInPathIdx === -1) return; - if (Math.abs(sourceInPathIdx - targetInPathIdx) === 1) { - graph.setItemState(edge, 'highlight', true); - } else { - graph.setItemState(edge, 'inactive', true); - } - }); - graph.getNodes().forEach((node) => { - if (!pathNodeMap[node.getID()]) { - graph.setItemState(node, 'inactive', true); - } - }); + if (path?.length) { + const pathNodeMap = {}; + path.forEach((id) => { + const pathNode = graph.findById(id); + pathNode.toFront(); + graph.setItemState(pathNode, 'highlight', true); + pathNodeMap[id] = true; + }); + graph.getEdges().forEach((edge) => { + const edgeModel = edge.getModel(); + const source = edgeModel.source; + const target = edgeModel.target; + const sourceInPathIdx = path.indexOf(source); + const targetInPathIdx = path.indexOf(target); + if (sourceInPathIdx === -1 || targetInPathIdx === -1) return; + if (Math.abs(sourceInPathIdx - targetInPathIdx) === 1) { + graph.setItemState(edge, 'highlight', true); + } else { + graph.setItemState(edge, 'inactive', true); + } + }); + graph.getNodes().forEach((node) => { + if (!pathNodeMap[node.getID()]) { + graph.setItemState(node, 'inactive', true); + } + }); + } }); }); diff --git a/packages/site/package.json b/packages/site/package.json index dbbab43065..513bd71c97 100644 --- a/packages/site/package.json +++ b/packages/site/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@antv/g6-site", - "version": "4.8.3", + "version": "4.8.4", "description": "G6 sites deployed on gh-pages", "keywords": [ "antv",