diff --git a/demos/arc-circle.html b/demos/arc-circle.html new file mode 100644 index 0000000000..50640f9b31 --- /dev/null +++ b/demos/arc-circle.html @@ -0,0 +1,60 @@ + + + + + 自定义弧形节点 + + + +
+ + + diff --git a/demos/collapse-expand-group.html b/demos/collapse-expand-group.html new file mode 100644 index 0000000000..d3aca772c9 --- /dev/null +++ b/demos/collapse-expand-group.html @@ -0,0 +1,141 @@ + + + + + 收起/展开群组 + + +
+ + + + + \ No newline at end of file diff --git a/demos/drag-group.html b/demos/drag-group.html new file mode 100644 index 0000000000..66b24e3a74 --- /dev/null +++ b/demos/drag-group.html @@ -0,0 +1,144 @@ + + + + + 拖动群组 + + +
+ + + + diff --git a/demos/linePoint.html b/demos/linePoint.html index 80c7e06bd0..b3ba27d8f5 100644 --- a/demos/linePoint.html +++ b/demos/linePoint.html @@ -1,10 +1,3 @@ - diff --git a/src/behavior/drag-group.js b/src/behavior/drag-group.js index 24c1034da0..ec1d7f8cfc 100644 --- a/src/behavior/drag-group.js +++ b/src/behavior/drag-group.js @@ -2,11 +2,10 @@ * @Author: moyee * @Date: 2019-07-31 14:36:15 * @LastEditors: moyee - * @LastEditTime: 2019-08-22 15:06:01 + * @LastEditTime: 2019-08-23 11:13:43 * @Description: file content */ - -import { merge } from 'lodash'; +const { merge } = require('lodash'); const delegateStyle = { fill: '#F3F9FF', @@ -119,7 +118,7 @@ module.exports = { * @return {boolean} false/true */ updatePosition(evt) { - if (!this.rectPos || !this.delegateShapeBBox) { + if (!this.delegateShapeBBox) { return false; } @@ -189,7 +188,8 @@ module.exports = { // step 1:先修改groupId中的节点位置 const nodeInGroup = groupNodes[groupId]; - + const groupOriginBBox = customGroupControll.getGroupOriginBBox(groupId); + const delegateShapeBBoxs = this.delegateShapeBBoxs[groupId]; const otherGroupId = []; nodeInGroup.forEach((nodeId, index) => { @@ -205,11 +205,9 @@ module.exports = { }; } - const groupOriginBBox = customGroupControll.getGroupOriginBBox(groupId); - // 群组拖动后节点的位置:deletateShape的最终位置-群组起始位置+节点位置 - const x = this.delegateShapeBBoxs[groupId].x - groupOriginBBox.x + this.nodePoint[index].x; - const y = this.delegateShapeBBoxs[groupId].y - groupOriginBBox.y + this.nodePoint[index].y; + const x = delegateShapeBBoxs.x - groupOriginBBox.x + this.nodePoint[index].x; + const y = delegateShapeBBoxs.y - groupOriginBBox.y + this.nodePoint[index].y; this.nodePoint[index] = { x, y @@ -238,7 +236,6 @@ module.exports = { const cy = (height + 2 * y) / 2; groupKeyShape.attr('x', cx); groupKeyShape.attr('y', cy); - customGroupControll.setGroupOriginBBox(id, groupKeyShape.getBBox()); }); @@ -288,8 +285,7 @@ module.exports = { }); self.shapeOrigin = { x: attrs.x, y: attrs.y }; } - delegateShape.set('capture', false); - this.rectPos = { ...self.shapeOrigin }; + // delegateShape.set('capture', false); self.delegateShapes[groupId] = delegateShape; self.delegateShapeBBoxs[groupId] = delegateShape.getBBox(); } else { @@ -299,9 +295,9 @@ module.exports = { const x = deltaX + shapeOrigin.x; const y = deltaY + shapeOrigin.y; - delegateShape.attr({ x, y }); - this.rectPos = { x, y }; - + // 将Canvas坐标转成视口坐标 + const point = graph.getPointByCanvas(x, y); + delegateShape.attr({ x: point.x, y: point.y }); self.delegateShapeBBoxs[groupId] = delegateShape.getBBox(); } diff --git a/src/behavior/drag-node-with-group.js b/src/behavior/drag-node-with-group.js index 3c8697326c..cebe176fd5 100644 --- a/src/behavior/drag-node-with-group.js +++ b/src/behavior/drag-node-with-group.js @@ -2,10 +2,9 @@ * @Author: moyee * @Date: 2019-06-27 18:12:06 * @LastEditors: moyee - * @LastEditTime: 2019-08-22 18:43:16 - * @Description: file content + * @LastEditTime: 2019-08-23 13:54:53 + * @Description: 有group的情况下,拖动节点的Behavior */ -const { mix } = require('../util'); const { merge } = require('lodash'); const { delegateStyle } = require('../global'); const body = document.body; @@ -228,6 +227,8 @@ module.exports = { x: cx, y: cy }); + + customGroupControll.setGroupOriginBBox(groupId, keyShape.getBBox()); } if (keyShape) { @@ -344,29 +345,6 @@ module.exports = { this.graph.paint(); } }, - _updateDelegate1(item, x, y) { - const self = this; - let shape = item.get('delegateShape'); - const bbox = item.get('keyShape').getBBox(); - if (!shape) { - const parent = self.graph.get('group'); - const attrs = mix({}, delegateStyle, this.delegateStyle); - // model上的x, y是相对于图形中心的,delegateShape是g实例,x,y是绝对坐标 - shape = parent.addShape('rect', { - attrs: { - width: bbox.width, - height: bbox.height, - x: x - bbox.width / 2, - y: y - bbox.height / 2, - ...attrs - } - }); - shape.set('capture', false); - item.set('delegateShape', shape); - } - shape.attr({ x: x - bbox.width / 2, y: y - bbox.height / 2 }); - this.graph.paint(); - }, /** * 更新拖动元素时的delegate * @param {Event} e 事件句柄 diff --git a/src/behavior/index.js b/src/behavior/index.js index cb68468b66..49c3678f8c 100644 --- a/src/behavior/index.js +++ b/src/behavior/index.js @@ -19,7 +19,7 @@ const behaviors = { 'brush-select': require('./brush-select'), 'drag-group': require('./drag-group'), 'drag-node-with-group': require('./drag-node-with-group'), - 'collspse-expand-group': require('./collapse-expand-group') + 'collapse-expand-group': require('./collapse-expand-group') }; Util.each(behaviors, (behavior, type) => { Behavior.registerBehavior(type, behavior); diff --git a/src/graph/controller/customGroup.js b/src/graph/controller/customGroup.js index 3191790691..ff4a208edd 100644 --- a/src/graph/controller/customGroup.js +++ b/src/graph/controller/customGroup.js @@ -2,12 +2,12 @@ * @Author: moyee * @Date: 2019-07-30 12:10:26 * @LastEditors: moyee - * @LastEditTime: 2019-08-22 18:41:33 + * @LastEditTime: 2019-08-23 11:44:32 * @Description: file content */ -import { merge, isString } from 'lodash'; +const { merge, isString } = require('lodash'); -export default class CustomGroup { +class CustomGroup { getDefaultCfg() { return { default: { @@ -15,7 +15,7 @@ export default class CustomGroup { stroke: '#A3B1BF', radius: 10, lineDash: [ 5, 5 ], - strokeOpacity: 0.92, + strokeOpacity: 0.9, fill: '#F3F9FF', fillOpacity: 0.8, opacity: 0.8 @@ -367,15 +367,22 @@ export default class CustomGroup { * @param {string} tmpNodeId 临时节点ID */ setGroupTmpNode(groupId, tmpNodeId) { - // TODO 需要调整 const graph = this.graph; const graphNodes = graph.get('groupNodes'); const groups = graph.get('groups'); - for (const data of groups) { - if (data.parentId === groupId) { - graphNodes[groupId].push(tmpNodeId); - this.setGroupTmpNode(data.parentId); - } + if (graphNodes[groupId].indexOf(tmpNodeId) < 0) { + graphNodes[groupId].push(tmpNodeId); + } + // 获取groupId的父群组 + const parentGroup = groups.filter(g => g.id === groupId); + let parentId = null; + if (parentGroup.length > 0) { + parentId = parentGroup[0].parentId; + } + + // 如果存在父群组,则把临时元素也添加到父群组中 + if (parentId) { + this.setGroupTmpNode(parentId, tmpNodeId); } } /** @@ -385,6 +392,7 @@ export default class CustomGroup { * @memberof ItemGroup */ collapseGroup(id) { + const self = this; const customGroup = this.getDeletageGroupById(id); const { nodeGroup, groupStyle } = customGroup; @@ -406,6 +414,9 @@ export default class CustomGroup { // 收起群组时候动画 keyShape.animate({ onFrame(ratio) { + if (ratio === 1) { + self.setGroupOriginBBox(id, keyShape.getBBox()); + } return { r: groupStyle.r - ratio * (groupStyle.r - r) }; @@ -445,13 +456,12 @@ export default class CustomGroup { }; // 将临时添加的节点加入到群组中,以便拖动节点时候线跟着拖动 - nodesInGroup.push(`${id}-custom-node`); - // this.setGroupTmpNode(id, `${id}-custom-node`); + // nodesInGroup.push(`${id}-custom-node`); + this.setGroupTmpNode(id, `${id}-custom-node`); this.updateEdgeInGroupLinks(id, sourceOutTargetInEdges, sourceInTargetOutEdges); } - // 获取群组中节点之间的所有边 const edgeAllInGroup = edges.filter(edge => { const model = edge.getModel(); @@ -538,6 +548,7 @@ export default class CustomGroup { */ expandGroup(id) { const graph = this.graph; + const self = this; const autoPaint = graph.get('autoPaint'); graph.setAutoPaint(false); @@ -560,12 +571,16 @@ export default class CustomGroup { // keyShape.attr('r', groupStyle.r + nodesInGroup.length * 10); keyShape.animate({ onFrame(ratio) { + if (ratio === 1) { + self.setGroupOriginBBox(id, keyShape.getBBox()); + } return { r: 30 + ratio * (groupStyle.r + nodesInGroup.length * 10 - 30) }; } }, 1000, 'easeCubic'); + // this.setGroupOriginBBox(id, keyShape.getBBox()); // 群组动画一会后再显示节点和边 setTimeout(() => { nodesInGroup.forEach(nodeId => { @@ -590,7 +605,7 @@ export default class CustomGroup { // 获取群组中节点之间的所有边 const edgeAllInGroup = edges.filter(edge => { const model = edge.getModel(); - return nodesInGroup.includes(model.source) && nodesInGroup.includes(model.target); + return nodesInGroup.includes(model.source) || nodesInGroup.includes(model.target); }); edgeAllInGroup.forEach(edge => { @@ -632,9 +647,8 @@ export default class CustomGroup { // 删除群组中的临时节点ID const tmpNodeModel = delegateNode.getModel(); - const index = nodesInGroup.indexOf(tmpNodeModel.id); - nodesInGroup.splice(index, 1); + this.deleteTmpNode(id, tmpNodeModel.id); graph.remove(delegateNode); delete this.delegateInGroup[id]; } @@ -642,6 +656,27 @@ export default class CustomGroup { graph.paint(); } + deleteTmpNode(groupId, tmpNodeId) { + const graph = this.graph; + const groups = graph.get('groups'); + const nodesInGroup = graph.get('groupNodes')[groupId]; + + const index = nodesInGroup.indexOf(tmpNodeId); + nodesInGroup.splice(index, 1); + + // 获取groupId的父群组 + const parentGroup = groups.filter(g => g.id === groupId); + let parentId = null; + if (parentGroup.length > 0) { + parentId = parentGroup[0].parentId; + } + + // 如果存在父群组,则把临时元素也添加到父群组中 + if (parentId) { + this.deleteTmpNode(parentId, tmpNodeId); + } + } + /** * 拆分群组 * @@ -687,3 +722,5 @@ export default class CustomGroup { this.delegateInGroup = {}; } } + +module.exports = CustomGroup; diff --git a/src/graph/controller/index.js b/src/graph/controller/index.js index 2d831abc49..16a7fd9eca 100644 --- a/src/graph/controller/index.js +++ b/src/graph/controller/index.js @@ -4,5 +4,5 @@ module.exports = { Mode: require('./mode'), Item: require('./item'), State: require('./state'), - CustomGroup: require('./customGroup').default + CustomGroup: require('./customGroup') }; diff --git a/src/graph/graph.js b/src/graph/graph.js index 1f81876f68..7f0919b5c1 100755 --- a/src/graph/graph.js +++ b/src/graph/graph.js @@ -9,7 +9,7 @@ * @fileOverview graph * @author huangtonger@aliyun.com */ -import { groupBy } from 'lodash'; +const { groupBy } = require('lodash'); const G = require('@antv/g/lib'); const EventEmitter = G.EventEmitter; const Util = require('../util'); diff --git a/test/unit/graph/graph-group-spec.js b/test/unit/graph/graph-group-spec.js index 9c2c056ddf..d744923212 100644 --- a/test/unit/graph/graph-group-spec.js +++ b/test/unit/graph/graph-group-spec.js @@ -2,7 +2,7 @@ * @Author: moyee * @Date: 2019-07-31 11:54:41 * @LastEditors: moyee - * @LastEditTime: 2019-08-22 15:47:52 + * @LastEditTime: 2019-08-23 14:16:27 * @Description: file content */ const expect = require('chai').expect; @@ -12,6 +12,21 @@ const div = document.createElement('div'); div.id = 'graph-group-spec'; document.body.appendChild(div); +G6.registerNode('circleNode', { + drawShape(cfg, group) { + const keyShape = group.addShape('circle', { + attrs: { + x: 0, + y: 0, + r: 30, + fill: '#87e8de' + } + }); + + return keyShape; + } +}, 'circle'); + describe('graph group', () => { const graph = new G6.Graph({ container: div, @@ -19,7 +34,13 @@ describe('graph group', () => { height: 1000, pixelRatio: 2, modes: { - default: [ 'drag-group', 'click-select', 'drag-node-with-group', 'collspse-expand-group' ] + default: [ 'zoom-canvas', 'drag-group', 'click-select', 'drag-node-with-group', 'collapse-expand-group' ] + }, + defaultNode: { + shape: 'circleNode' + }, + defaultEdge: { + color: '#bae7ff' } }); @@ -87,8 +108,7 @@ describe('graph group', () => { groupId: 'group3', label: 'rect', x: 100, - y: 300, - shape: 'rect' + y: 300 }, { id: 'node1', @@ -121,6 +141,7 @@ describe('graph group', () => { { id: 'node7', groupId: 'p1', + label: 'node7-p1', x: 200, y: 200 },