diff --git a/CHANGELOG.md b/CHANGELOG.md index 9480aa74781..0d7398bbdd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # ChangeLog +### 4.7.8 + +- feat: pointPadding config for loop edges with non-circle nodes, closes: #3974; +- fix: image lost while updating the size for an image node, closes: #3938; + ### 4.7.7 - feat: getContentPlaceholder and getTitlePlaceholder for Annotation plugin; diff --git a/packages/core/package.json b/packages/core/package.json index 69b38197ebf..32ac658d7ff 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-core", - "version": "0.7.7", + "version": "0.7.8", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", diff --git a/packages/core/src/element/shapeBase.ts b/packages/core/src/element/shapeBase.ts index cd634fb49ba..60d12df2d17 100644 --- a/packages/core/src/element/shapeBase.ts +++ b/packages/core/src/element/shapeBase.ts @@ -79,9 +79,7 @@ export const shapeBase: ShapeOptions = { return {}; }, getOptions(cfg: ModelConfig, updateType?: UpdateType): ModelConfig { - if (updateType === 'move' || updateType?.includes('bbox')) { - return {}; - } + if (updateType === 'move' || updateType?.includes('bbox')) return cfg; return deepMix( {}, this.options, diff --git a/packages/core/src/global.ts b/packages/core/src/global.ts index 70431a048f6..eb5f8f39f46 100644 --- a/packages/core/src/global.ts +++ b/packages/core/src/global.ts @@ -64,7 +64,7 @@ const colorSet = { }; export default { - version: '0.7.7', + version: '0.7.8', rootContainerClassName: 'root-container', nodeContainerClassName: 'node-container', edgeContainerClassName: 'edge-container', diff --git a/packages/core/src/types/index.ts b/packages/core/src/types/index.ts index 0249cd4b7a5..58bbd87fbff 100644 --- a/packages/core/src/types/index.ts +++ b/packages/core/src/types/index.ts @@ -298,6 +298,8 @@ export type LoopConfig = Partial<{ position: string; // 如果逆时针画,交换起点和终点 clockwise: boolean; + // 对于非圆形节点设置的连接点与节点中心坐标(`top-right`,`bottom-right`,`top-left`,`bottom-left`较特殊,为四个角坐标)在 x 轴或 y 轴方向的偏移量,默认为  `节点宽高中最小值的1/4` + pointPadding: number; }>; export interface LayoutConfig { diff --git a/packages/core/src/util/graphic.ts b/packages/core/src/util/graphic.ts index 721a78d318b..ff977f152a0 100644 --- a/packages/core/src/util/graphic.ts +++ b/packages/core/src/util/graphic.ts @@ -11,7 +11,7 @@ import { ComboTree, ComboConfig, ICombo, - GraphAnimateConfig + GraphAnimateConfig, } from '../types'; import { applyMatrix } from './math'; import letterAspectRatio from './letterAspectRatio'; @@ -83,93 +83,146 @@ export const getLoopCfgs = (cfg: EdgeData): EdgeData => { let startPoint = [cfg.startPoint.x, cfg.startPoint.y]; let endPoint = [cfg.endPoint.x, cfg.endPoint.y]; - let rstart = bbox.height / 2; - let rend = bbox.height / 2; + let halfOfHeight = bbox.height / 2; + let halfOfWidth = bbox.width / 2; + let rstart = halfOfHeight; + let rend = halfOfHeight; + let sinDeltaStart = rstart * SELF_LINK_SIN; let cosDeltaStart = rstart * SELF_LINK_COS; let sinDeltaEnd = rend * SELF_LINK_SIN; let cosDeltaEnd = rend * SELF_LINK_COS; + const shapeType = keyShape.get('type'); + + // 美观考虑,pointPadding 默认取宽高中最小的1/4 + const defaultPointPadding = Math.min(halfOfHeight / 2, halfOfWidth / 2); + const maxPointPadding = Math.min(halfOfHeight, halfOfWidth); + + // 对于非圆形节点设置的连接点与节点中心坐标(`top-right`,`bottom-right`,`top-left`,`bottom-left`较特殊,为四个角坐标)在 x 轴或 y 轴方向的偏移量,默认为  `节点宽高中最小值的1/4` + const pointPadding = loopCfg?.pointPadding + ? Math.min(maxPointPadding, loopCfg?.pointPadding) + : defaultPointPadding; // 如果定义了锚点的,直接用锚点坐标,否则,根据自环的 cfg 计算 if (startPoint[0] === endPoint[0] && startPoint[1] === endPoint[1]) { switch (position) { case 'top': - startPoint = [center[0] - sinDeltaStart, center[1] - cosDeltaStart]; - endPoint = [center[0] + sinDeltaEnd, center[1] - cosDeltaEnd]; + if (shapeType === 'circle') { + startPoint = [center[0] - sinDeltaStart, center[1] - cosDeltaStart]; + endPoint = [center[0] + sinDeltaEnd, center[1] - cosDeltaEnd]; + } else { + startPoint = [center[0] - pointPadding, center[1] - halfOfHeight]; + endPoint = [center[0] + pointPadding, center[1] - halfOfHeight]; + } break; case 'top-right': - rstart = bbox.height / 2; - rend = bbox.width / 2; - sinDeltaStart = rstart * SELF_LINK_SIN; - cosDeltaStart = rstart * SELF_LINK_COS; - sinDeltaEnd = rend * SELF_LINK_SIN; - cosDeltaEnd = rend * SELF_LINK_COS; - startPoint = [center[0] + sinDeltaStart, center[1] - cosDeltaStart]; - endPoint = [center[0] + cosDeltaEnd, center[1] - sinDeltaEnd]; + rstart = halfOfHeight; + rend = halfOfWidth; + if (shapeType === 'circle') { + sinDeltaStart = rstart * SELF_LINK_SIN; + cosDeltaStart = rstart * SELF_LINK_COS; + sinDeltaEnd = rend * SELF_LINK_SIN; + cosDeltaEnd = rend * SELF_LINK_COS; + startPoint = [center[0] + sinDeltaStart, center[1] - cosDeltaStart]; + endPoint = [center[0] + cosDeltaEnd, center[1] - sinDeltaEnd]; + } else { + startPoint = [center[0] + halfOfWidth - pointPadding, center[1] - halfOfHeight]; + endPoint = [center[0] + halfOfWidth, center[1] - halfOfHeight + pointPadding]; + } break; case 'right': - rstart = bbox.width / 2; - rend = bbox.width / 2; - sinDeltaStart = rstart * SELF_LINK_SIN; - cosDeltaStart = rstart * SELF_LINK_COS; - sinDeltaEnd = rend * SELF_LINK_SIN; - cosDeltaEnd = rend * SELF_LINK_COS; - startPoint = [center[0] + cosDeltaStart, center[1] - sinDeltaStart]; - endPoint = [center[0] + cosDeltaEnd, center[1] + sinDeltaEnd]; + rstart = halfOfWidth; + rend = halfOfWidth; + if (shapeType === 'circle') { + sinDeltaStart = rstart * SELF_LINK_SIN; + cosDeltaStart = rstart * SELF_LINK_COS; + sinDeltaEnd = rend * SELF_LINK_SIN; + cosDeltaEnd = rend * SELF_LINK_COS; + startPoint = [center[0] + cosDeltaStart, center[1] - sinDeltaStart]; + endPoint = [center[0] + cosDeltaEnd, center[1] + sinDeltaEnd]; + } else { + startPoint = [center[0] + halfOfWidth, center[1] - pointPadding]; + endPoint = [center[0] + halfOfWidth, center[1] + pointPadding]; + } break; case 'bottom-right': - rstart = bbox.width / 2; - rend = bbox.height / 2; - sinDeltaStart = rstart * SELF_LINK_SIN; - cosDeltaStart = rstart * SELF_LINK_COS; - sinDeltaEnd = rend * SELF_LINK_SIN; - cosDeltaEnd = rend * SELF_LINK_COS; - startPoint = [center[0] + cosDeltaStart, center[1] + sinDeltaStart]; - endPoint = [center[0] + sinDeltaEnd, center[1] + cosDeltaEnd]; + rstart = halfOfWidth; + rend = halfOfHeight; + if (shapeType === 'circle') { + sinDeltaStart = rstart * SELF_LINK_SIN; + cosDeltaStart = rstart * SELF_LINK_COS; + sinDeltaEnd = rend * SELF_LINK_SIN; + cosDeltaEnd = rend * SELF_LINK_COS; + startPoint = [center[0] + cosDeltaStart, center[1] + sinDeltaStart]; + endPoint = [center[0] + sinDeltaEnd, center[1] + cosDeltaEnd]; + } else { + startPoint = [center[0] + halfOfWidth, center[1] + halfOfHeight - pointPadding]; + endPoint = [center[0] + halfOfWidth - pointPadding, center[1] + halfOfHeight]; + } break; case 'bottom': - rstart = bbox.height / 2; - rend = bbox.height / 2; - sinDeltaStart = rstart * SELF_LINK_SIN; - cosDeltaStart = rstart * SELF_LINK_COS; - sinDeltaEnd = rend * SELF_LINK_SIN; - cosDeltaEnd = rend * SELF_LINK_COS; - startPoint = [center[0] + sinDeltaStart, center[1] + cosDeltaStart]; - endPoint = [center[0] - sinDeltaEnd, center[1] + cosDeltaEnd]; + rstart = halfOfHeight; + rend = halfOfHeight; + if (shapeType === 'circle') { + sinDeltaStart = rstart * SELF_LINK_SIN; + cosDeltaStart = rstart * SELF_LINK_COS; + sinDeltaEnd = rend * SELF_LINK_SIN; + cosDeltaEnd = rend * SELF_LINK_COS; + startPoint = [center[0] + sinDeltaStart, center[1] + cosDeltaStart]; + endPoint = [center[0] - sinDeltaEnd, center[1] + cosDeltaEnd]; + } else { + startPoint = [center[0] - pointPadding, center[1] + halfOfHeight]; + endPoint = [center[0] + pointPadding, center[1] + halfOfHeight]; + } break; case 'bottom-left': - rstart = bbox.height / 2; - rend = bbox.width / 2; - sinDeltaStart = rstart * SELF_LINK_SIN; - cosDeltaStart = rstart * SELF_LINK_COS; - sinDeltaEnd = rend * SELF_LINK_SIN; - cosDeltaEnd = rend * SELF_LINK_COS; - startPoint = [center[0] - sinDeltaStart, center[1] + cosDeltaStart]; - endPoint = [center[0] - cosDeltaEnd, center[1] + sinDeltaEnd]; + rstart = halfOfHeight; + rend = halfOfWidth; + if (shapeType === 'circle') { + sinDeltaStart = rstart * SELF_LINK_SIN; + cosDeltaStart = rstart * SELF_LINK_COS; + sinDeltaEnd = rend * SELF_LINK_SIN; + cosDeltaEnd = rend * SELF_LINK_COS; + startPoint = [center[0] - sinDeltaStart, center[1] + cosDeltaStart]; + endPoint = [center[0] - cosDeltaEnd, center[1] + sinDeltaEnd]; + } else { + startPoint = [center[0] - halfOfWidth, center[1] + halfOfHeight - pointPadding]; + endPoint = [center[0] - halfOfWidth + pointPadding, center[1] + halfOfHeight]; + } break; case 'left': - rstart = bbox.width / 2; - rend = bbox.width / 2; - sinDeltaStart = rstart * SELF_LINK_SIN; - cosDeltaStart = rstart * SELF_LINK_COS; - sinDeltaEnd = rend * SELF_LINK_SIN; - cosDeltaEnd = rend * SELF_LINK_COS; - startPoint = [center[0] - cosDeltaStart, center[1] + sinDeltaStart]; - endPoint = [center[0] - cosDeltaEnd, center[1] - sinDeltaEnd]; + rstart = halfOfWidth; + rend = halfOfWidth; + if (shapeType === 'circle') { + sinDeltaStart = rstart * SELF_LINK_SIN; + cosDeltaStart = rstart * SELF_LINK_COS; + sinDeltaEnd = rend * SELF_LINK_SIN; + cosDeltaEnd = rend * SELF_LINK_COS; + startPoint = [center[0] - cosDeltaStart, center[1] + sinDeltaStart]; + endPoint = [center[0] - cosDeltaEnd, center[1] - sinDeltaEnd]; + } else { + startPoint = [center[0] - halfOfWidth, center[1] - pointPadding]; + endPoint = [center[0] - halfOfWidth, center[1] + pointPadding]; + } break; case 'top-left': - rstart = bbox.width / 2; - rend = bbox.height / 2; - sinDeltaStart = rstart * SELF_LINK_SIN; - cosDeltaStart = rstart * SELF_LINK_COS; - sinDeltaEnd = rend * SELF_LINK_SIN; - cosDeltaEnd = rend * SELF_LINK_COS; - startPoint = [center[0] - cosDeltaStart, center[1] - sinDeltaStart]; - endPoint = [center[0] - sinDeltaEnd, center[1] - cosDeltaEnd]; + rstart = halfOfWidth; + rend = halfOfHeight; + if (shapeType === 'circle') { + sinDeltaStart = rstart * SELF_LINK_SIN; + cosDeltaStart = rstart * SELF_LINK_COS; + sinDeltaEnd = rend * SELF_LINK_SIN; + cosDeltaEnd = rend * SELF_LINK_COS; + startPoint = [center[0] - cosDeltaStart, center[1] - sinDeltaStart]; + endPoint = [center[0] - sinDeltaEnd, center[1] - cosDeltaEnd]; + } else { + startPoint = [center[0] - halfOfWidth + pointPadding, center[1] - halfOfHeight]; + endPoint = [center[0] - halfOfWidth, center[1] - halfOfHeight + pointPadding]; + } break; default: - rstart = bbox.width / 2; - rend = bbox.width / 2; + rstart = halfOfWidth; + rend = halfOfWidth; sinDeltaStart = rstart * SELF_LINK_SIN; cosDeltaStart = rstart * SELF_LINK_COS; sinDeltaEnd = rend * SELF_LINK_SIN; @@ -381,7 +434,7 @@ export const truncateLabelByLength = (text: string, length: number) => { return text; } return text.substring(0, length) + '...'; -} +}; /** * construct the trees from combos data @@ -600,7 +653,11 @@ export const reconstructTree = ( return trees; }; -export const getComboBBox = (children: ComboTree[], graph: IAbstractGraph, combo?: ICombo): BBox => { +export const getComboBBox = ( + children: ComboTree[], + graph: IAbstractGraph, + combo?: ICombo, +): BBox => { const comboBBox = { minX: Infinity, minY: Infinity, @@ -625,7 +682,7 @@ export const getComboBBox = (children: ComboTree[], graph: IAbstractGraph, combo x: x, y: y, width: undefined, - height: undefined + height: undefined, }; } @@ -671,26 +728,26 @@ export const shouldRefreshEdge = (cfg) => { export const cloneBesidesImg = (obj) => { const clonedObj = {}; - Object.keys(obj).forEach(key1 => { + Object.keys(obj).forEach((key1) => { const obj2 = obj[key1]; if (isObject(obj2) && !isArray(obj2)) { const clonedObj2 = {}; - Object.keys(obj2).forEach(key2 => { + Object.keys(obj2).forEach((key2) => { const v = obj2[key2]; if (key2 === 'img' && !isString(v)) return; clonedObj2[key2] = clone(v); - }) + }); clonedObj[key1] = clonedObj2; } else { clonedObj[key1] = clone(obj2); } }); return clonedObj; -} +}; export const getAnimateCfgWithCallback = ({ animateCfg, - callback + callback, }: { animateCfg: GraphAnimateConfig; callback: () => void; @@ -699,7 +756,7 @@ export const getAnimateCfgWithCallback = ({ if (!animateCfg) { animateConfig = { duration: 500, - callback + callback, }; } else { animateConfig = clone(animateCfg); @@ -708,10 +765,10 @@ export const getAnimateCfgWithCallback = ({ animateConfig.callback = () => { callback(); animateCfgCallback(); - } + }; } else { animateConfig.callback = callback; } } return animateConfig; -} +}; diff --git a/packages/element/package.json b/packages/element/package.json index 266152c0ea0..84e8cae3d72 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-element", - "version": "0.7.7", + "version": "0.7.8", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", @@ -61,7 +61,7 @@ }, "dependencies": { "@antv/g-base": "^0.5.1", - "@antv/g6-core": "0.7.7", + "@antv/g6-core": "0.7.8", "@antv/util": "~2.0.5" }, "devDependencies": { diff --git a/packages/g6/package.json b/packages/g6/package.json index 2134e2588a8..5a67a8e8129 100644 --- a/packages/g6/package.json +++ b/packages/g6/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6", - "version": "4.7.7", + "version": "4.7.8", "description": "A Graph Visualization Framework in JavaScript", "keywords": [ "antv", @@ -66,7 +66,7 @@ ] }, "dependencies": { - "@antv/g6-pc": "0.7.7" + "@antv/g6-pc": "0.7.8" }, "devDependencies": { "@babel/core": "^7.7.7", diff --git a/packages/g6/src/index.ts b/packages/g6/src/index.ts index a961bec8e30..2ef104c2706 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.7.7'; +G6.version = '4.7.8'; export * from '@antv/g6-pc'; export default G6; -export const version = '4.7.7'; +export const version = '4.7.8'; diff --git a/packages/pc/package.json b/packages/pc/package.json index 4af4f5c4c99..761ce5195ef 100644 --- a/packages/pc/package.json +++ b/packages/pc/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-pc", - "version": "0.7.7", + "version": "0.7.8", "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.7.7", - "@antv/g6-element": "0.7.7", - "@antv/g6-plugin": "0.7.7", + "@antv/g6-core": "0.7.8", + "@antv/g6-element": "0.7.8", + "@antv/g6-plugin": "0.7.8", "@antv/hierarchy": "^0.6.7", "@antv/layout": "^0.3.0", "@antv/matrix-util": "^3.1.0-beta.3", diff --git a/packages/pc/src/global.ts b/packages/pc/src/global.ts index 132d29a7775..86eba0a578b 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.7.7', + version: '0.7.8', rootContainerClassName: 'root-container', nodeContainerClassName: 'node-container', edgeContainerClassName: 'edge-container', diff --git a/packages/pc/tests/unit/issues-loop-spec.ts b/packages/pc/tests/unit/issues-loop-spec.ts new file mode 100644 index 00000000000..1dd5809c67e --- /dev/null +++ b/packages/pc/tests/unit/issues-loop-spec.ts @@ -0,0 +1,372 @@ +import G6, { Graph } from '../../src'; +import { Arrow } from '../../src'; + +const div = document.createElement('div'); +div.id = 'container'; +document.body.appendChild(div); + + +describe('issues', () => { + it('basic test ui', () => { + + const nodes = [ + { + id: '1', + type: 'rect', + color: '#333', + x: 100, + y: 200, + size: [80, 80], + label: 'rect', + }, + { + id: '2', + type: 'rect', + color: '#333', + x: 100, + y: 400, + size: [400, 80], + label: 'rect', + }, + { + id: '3', + type: 'circle', + color: '#666', + x: 100, + y: 700, + size: [200, 40], + label: 'circle', + }, + ]; + + const directions = [ + 'top-left', + 'top', + 'top-right', + 'right', + 'bottom-right', + 'bottom', + 'bottom-left', + 'left', + ]; + + const edges = []; + nodes.forEach((node) => { + const perNodeEdge = directions.map((currentDirection) => ({ + source: node.id, + target: node.id, + type: 'loop', + loopCfg: { + position: currentDirection, + dist: 20, + pointPadding: 15, + clockwise: true, + }, + })); + edges.push(...perNodeEdge); + }); + + const data = { + nodes, + edges, + }; + const graph = new G6.Graph({ + container: div, + width: 800, + height: 800, + fitCenter: true, + defaultNode: { + type: 'rect', + }, + }); + + graph.data(data); + graph.render(); + }); + + it('test position right calc pointPadding value is ok ', () => { + const node = + { + id: '1', + type: 'rect', + color: '#333', + x: 100, + y: 200, + size: [80, 80], + label: 'rect', + style: { + lineWidth: 0 + }, + } + const edge = { + source: '1', + target: '1', + type: 'loop', + loopCfg: { + position: 'right', + dist: 20, + pointPadding: 15, + }, + } + + const data = { + nodes: [node], + edges: [edge], + }; + + const graph = new G6.Graph({ + container: div, + width: 800, + height: 800, + fitCenter: true, + }); + + const center = [node.x, node.y] + const halfOfWidth = node.size[0] / 2 + const halfOfHeight = node.size[1] / 2 + const pointPadding = edge.loopCfg.pointPadding + graph.data(data); + graph.render(); + const startPoint = [center[0] + halfOfWidth, center[1] - pointPadding]; + const endPoint = [center[0] + halfOfWidth, center[1] + pointPadding]; + + const { edges } = data + const currentEdges = edges[0] + + //@ts-ignore + expect([currentEdges.startPoint.x, currentEdges.startPoint.y]).toEqual(startPoint); + //@ts-ignore + expect([currentEdges.endPoint.x, currentEdges.endPoint.y]).toEqual(endPoint); + }) + + it('test position top-right calc pointPadding value is ok ', () => { + const node = + { + id: '1', + type: 'rect', + color: '#333', + x: 100, + y: 200, + size: [80, 80], + label: 'rect', + style: { + lineWidth: 0 + }, + } + const edge = { + source: '1', + target: '1', + type: 'loop', + loopCfg: { + position: 'top-right', + dist: 20, + pointPadding: 15, + }, + } + + const data = { + nodes: [node], + edges: [edge], + }; + + const graph = new G6.Graph({ + container: div, + width: 800, + height: 800, + fitCenter: true, + }); + + + const center = [node.x, node.y] + const halfOfWidth = node.size[0] / 2 + const halfOfHeight = node.size[1] / 2 + const pointPadding = edge.loopCfg.pointPadding + graph.data(data); + graph.render(); + + const startPoint = [center[0] + halfOfWidth - pointPadding, center[1] - halfOfHeight]; + const endPoint = [center[0] + halfOfWidth, center[1] - halfOfHeight + pointPadding]; + + const { edges } = data + const currentEdges = edges[0] + + //@ts-ignore + expect([currentEdges.startPoint.x, currentEdges.startPoint.y]).toEqual(startPoint); + //@ts-ignore + expect([currentEdges.endPoint.x, currentEdges.endPoint.y]).toEqual(endPoint); + }) + + + it('test unset pointPadding and final pointPadding calc is ok', () => { + const node = + { + id: '1', + type: 'rect', + color: '#333', + x: 100, + y: 200, + size: [80, 80], + label: 'rect', + style: { + lineWidth: 0 + }, + } + const edge = { + source: '1', + target: '1', + type: 'loop', + loopCfg: { + position: 'top-right', + dist: 20, + }, + } + + const data = { + nodes: [node], + edges: [edge], + }; + + const graph = new G6.Graph({ + container: div, + width: 800, + height: 800, + fitCenter: true, + }); + + const center = [node.x, node.y] + const halfOfWidth = node.size[0] / 2 + const halfOfHeight = node.size[1] / 2 + // 预期 pointPadding 为 20 + const pointPadding = 20 + graph.data(data); + graph.render(); + + const startPoint = [center[0] + halfOfWidth - pointPadding, center[1] - halfOfHeight]; + const endPoint = [center[0] + halfOfWidth, center[1] - halfOfHeight + pointPadding]; + + const { edges } = data + const currentEdges = edges[0] + + //@ts-ignore + expect([currentEdges.startPoint.x, currentEdges.startPoint.y]).toEqual(startPoint); + //@ts-ignore + expect([currentEdges.endPoint.x, currentEdges.endPoint.y]).toEqual(endPoint); + }) + + it('test set pointPadding greater than minimum height and width minimum value , final pointPadding calc is ok', () => { + const node = + { + id: '1', + type: 'rect', + color: '#333', + x: 100, + y: 200, + size: [80, 80], + label: 'rect', + style: { + lineWidth: 0 + }, + } + const edge = { + source: '1', + target: '1', + type: 'loop', + loopCfg: { + position: 'top-right', + dist: 20, + clockwise: true, + pointPadding: 1000, + }, + } + + const data = { + nodes: [node], + edges: [edge], + }; + + const graph = new G6.Graph({ + container: div, + width: 800, + height: 800, + fitCenter: true, + }); + + const center = [node.x, node.y] + const halfOfWidth = node.size[0] / 2 + const halfOfHeight = node.size[1] / 2 + // 预期 pointPadding 为 40 + const pointPadding = 40 + graph.data(data); + graph.render(); + + const startPoint = [center[0] + halfOfWidth - pointPadding, center[1] - halfOfHeight]; + const endPoint = [center[0] + halfOfWidth, center[1] - halfOfHeight + pointPadding]; + + const { edges } = data + const currentEdges = edges[0] + + //@ts-ignore + expect([currentEdges.startPoint.x, currentEdges.startPoint.y]).toEqual(startPoint); + //@ts-ignore + expect([currentEdges.endPoint.x, currentEdges.endPoint.y]).toEqual(endPoint); + }) + + + it('test set clockwise => true, calc pointPadding is ok', () => { + const node = + { + id: '1', + type: 'rect', + color: '#333', + x: 100, + y: 200, + size: [80, 80], + label: 'rect', + style: { + lineWidth: 0 + }, + } + const edge = { + source: '1', + target: '1', + type: 'loop', + loopCfg: { + position: 'top-right', + dist: 20, + clockwise: true, + pointPadding: 1000, + }, + } + + const data = { + nodes: [node], + edges: [edge], + }; + + const graph = new G6.Graph({ + container: div, + width: 800, + height: 800, + fitCenter: true, + }); + + const center = [node.x, node.y] + const halfOfWidth = node.size[0] / 2 + const halfOfHeight = node.size[1] / 2 + // 预期 pointPadding 为 40 + const pointPadding = 40 + graph.data(data); + graph.render(); + + const startPoint = [center[0] + halfOfWidth - pointPadding, center[1] - halfOfHeight]; + const endPoint = [center[0] + halfOfWidth, center[1] - halfOfHeight + pointPadding]; + + const { edges } = data + const currentEdges = edges[0] + + //@ts-ignore + expect([currentEdges.startPoint.x, currentEdges.startPoint.y]).toEqual(startPoint); + //@ts-ignore + expect([currentEdges.endPoint.x, currentEdges.endPoint.y]).toEqual(endPoint); + }) +}); diff --git a/packages/plugin/package.json b/packages/plugin/package.json index d3cf3cd4fc6..d53abc45446 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6-plugin", - "version": "0.7.7", + "version": "0.7.8", "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.7.7", - "@antv/g6-element": "0.7.7", + "@antv/g6-core": "0.7.8", + "@antv/g6-element": "0.7.8", "@antv/matrix-util": "^3.1.0-beta.3", "@antv/scale": "^0.3.4", "@antv/util": "^2.0.9", diff --git a/packages/site/docs/manual/middle/elements/edges/built-in/loop.en.md b/packages/site/docs/manual/middle/elements/edges/built-in/loop.en.md index 0292f15a9a8..de9d9bc0306 100644 --- a/packages/site/docs/manual/middle/elements/edges/built-in/loop.en.md +++ b/packages/site/docs/manual/middle/elements/edges/built-in/loop.en.md @@ -132,11 +132,12 @@ const graph = new G6.Graph({ ### loopCfg -`loopCfg` is an object that configures the direction, height, and clockwise. +`loopCfg` is an object that configures the direction, height, and clockwise, connection point start and end position. - `position`: The relative position to the source/target node. Options: `top`, `top-right`, `right`,`bottom-right`, `bottom`, `bottom-left`, `left`, `top-left`. `top` by default. - `dist`: The distance between the keyShape of the source/target node to the highest position of the loop. It is equal to the height of the source/target node by default. - `clockwise`: Whether to draw the loop clockwisely. `true` by default +- `pointPadding`: For non-circular nodes, the offset between the connection point and the node center coordinates ('top right', 'bottom right', 'top left', 'bottom left', which are special, four angular coordinates) in the x-axis or y-axis direction, the default value is' 1/4 of the minimum value of node width and height '. *Supported by v4.7.8.* Base on the code in [style](#style) section, we add `loopCfg` to `defaultEdge`.
img @@ -152,6 +153,7 @@ const graph = new G6.Graph({ position: 'left', dist: 100, clockwise: false, + pointPadding: 15, }, }, }); diff --git a/packages/site/docs/manual/middle/elements/edges/built-in/loop.zh.md b/packages/site/docs/manual/middle/elements/edges/built-in/loop.zh.md index 8c3114b5ba4..c2dc5753aab 100644 --- a/packages/site/docs/manual/middle/elements/edges/built-in/loop.zh.md +++ b/packages/site/docs/manual/middle/elements/edges/built-in/loop.zh.md @@ -132,11 +132,12 @@ const graph = new G6.Graph({ ### 自环特殊配置  loopCfg -Object 类型。通过 `loopCfg`  配置自环的方位、高度、顺逆时针。 +Object 类型。通过 `loopCfg`  配置自环的方位、高度、顺逆时针、连接起始点位置。 - `position`: 指定自环与节点的相对位置。默认为:`top`。支持的值有:`top`, `top-right`, `right`,`bottom-right`, `bottom`, `bottom-left`, `left`, `top-left` - `dist`: 从节点 keyShape 的边缘到自环最顶端的位置,用于指定自环的曲度,默认为节点的高度。 - `clockwise`: 指定是否顺时针画环,默认为  `true`。 +- `pointPadding`: 对于非圆形节点设置的连接点与节点中心坐标(`top-right`,`bottom-right`,`top-left`,`bottom-left`较特殊,为四个角坐标)在 x 轴或 y 轴方向的偏移量,默认为  `节点宽高中最小值的1/4`,*v4.7.8 后支持*。 基于上面 [样式属性 style](#XQFb2) 中的代码,下面代码在 `defaultEdge` 中增加了  `loopCfg`  配置项进行文本的配置,使之达到如下图效果。
img @@ -152,6 +153,7 @@ const graph = new G6.Graph({ position: 'left', dist: 100, clockwise: false, + pointPadding: 15, }, }, }); diff --git a/packages/site/package.json b/packages/site/package.json index 7ba2f68013c..72dfe14fe7b 100644 --- a/packages/site/package.json +++ b/packages/site/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@antv/g6-site", - "version": "4.7.7", + "version": "4.7.8", "description": "G6 sites deployed on gh-pages", "keywords": [ "antv", @@ -36,7 +36,7 @@ "dependencies": { "@ant-design/icons": "^4.0.6", "@antv/chart-node-g6": "^0.0.3", - "@antv/g6": "4.7.7", + "@antv/g6": "4.7.8", "@antv/gatsby-theme-antv": "1.1.15", "@antv/util": "^2.0.9", "@antv/vis-predict-engine": "^0.1.1",