fix: wrong dropped position for drag-combo with enableDelegate, close… (#3821)

* fix: wrong dropped position for drag-combo with enableDelegate, closes: #3810; fix: stack for drag-combo with onlyChangeComboSize, closes: #3801; fix: stack updateLayout, closes: #3765; fix: drag-canvas and zoom-canvas with enableOptimize show a hiden shape which is controlled by state, closes: #3635;

* chore: refine CHANGELOG

* fix: only regenerate id when id is undefined (#3670)

* fix: only regenerate id when id is undefined

* feat: normalize id

* fix: findAllByState should not select hidden nodes (#3784)

* fix: findAllByState should not select hidden nodes

* clean up

* clean up

* update interface

* fix: react node typings inaccuracy (#3790)

* docs: react node match typings

* fix: react node optional `style` properties

* fix: react node `React 18` compatibility

* docs: fix a typo

* feat: rework animated fitView (#3796)

* Add lerpArray function

* Move getAnimateCfgWithCallback function

* Reimplement animated fitView

* Animated fitview rework

* Remove unused import

* Fix graph warp when hitting max/min zoom

* Avoid multiple transforms

* docs: update CHANGELOG (#3822)

* docs: update CHANGELOG

* chore: upgrade version num

Co-authored-by: AlbertAz <ziyuximing@163.com>
Co-authored-by: femiabdul <83455275+femiabdul@users.noreply.github.com>
Co-authored-by: MikalaiLappo <102466089+MikalaiLappo@users.noreply.github.com>
Co-authored-by: Fabio Tacchelli <fabio.tacchelli@gmail.com>
This commit is contained in:
Yanyan Wang 2022-08-01 16:10:53 +08:00 committed by GitHub
parent 5a7681c85f
commit 5407845e42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 392 additions and 160 deletions

View File

@ -1,5 +1,16 @@
# ChangeLog
#### 4.6.16
- feat: ID check;
- feat: fitView with animation;
- feat: findAllByState with additional filter;
- fix: wrong dropped position for drag-combo with enableDelegate, closes: #3810;
- fix: stack for drag-combo with onlyChangeComboSize, closes: #3801;
- fix: stack updateLayout, closes: #3765;
- fix: drag-canvas and zoom-canvas with enableOptimize show a hidden shape which is controlled by state, closes: #3635;
- fix: typing problem for react node;
#### 4.6.15
- fix: fitView does not zoom the graph with animate true;

View File

@ -1,6 +1,6 @@
{
"name": "@antv/g6-core",
"version": "0.6.15",
"version": "0.6.16",
"description": "A Graph Visualization Framework in JavaScript",
"keywords": [
"antv",

View File

@ -64,7 +64,7 @@ const colorSet = {
};
export default {
version: '0.6.15',
version: '0.6.16',
rootContainerClassName: 'root-container',
nodeContainerClassName: 'node-container',
edgeContainerClassName: 'edge-container',

View File

@ -1,11 +1,12 @@
import { AbstractCanvas } from '@antv/g-base';
import { AbstractCanvas, BBox } from '@antv/g-base';
import { Point, IGroup } from '@antv/g-base';
import { isNumber, isString } from '@antv/util';
import { modifyCSS } from '@antv/dom-util';
import { Item, Matrix, Padding, GraphAnimateConfig, IEdge, FitViewRules } from '../../types';
import { formatPadding } from '../../util/base';
import { applyMatrix, invertMatrix } from '../../util/math';
import { applyMatrix, invertMatrix, lerpArray } from '../../util/math';
import { IAbstractGraph } from '../../interface/graph';
import { transform } from '@antv/matrix-util/lib/ext';
import { getAnimateCfgWithCallback } from '../../util/graphic';
export default class ViewController {
private graph: IAbstractGraph;
@ -44,6 +45,49 @@ export default class ViewController {
graph.translate(viewCenter.x - groupCenter.x, viewCenter.y - groupCenter.y, animate, animateCfg);
}
private animatedFitView(group: IGroup, startMatrix: number[], animateCfg: GraphAnimateConfig, bbox: BBox, viewCenter: Point, groupCenter: Point, ratio: number): void {
const { graph } = this;
animateCfg = animateCfg ? animateCfg : { duration: 500, easing: 'easeCubic' };
// start from the default matrix
const matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1];
// Translate
const vx = bbox.x + viewCenter.x - groupCenter.x - bbox.minX;
const vy = bbox.y + viewCenter.y - groupCenter.y - bbox.minY;
const translatedMatrix = transform(matrix, [['t', vx, vy]]);
// Zoom
const minZoom: number = graph.get('minZoom');
const maxZoom: number = graph.get('maxZoom');
let realRatio = ratio;
if (minZoom && ratio < minZoom) {
realRatio = minZoom;
console.warn('fitview failed, ratio out of range, ratio: %f', ratio, 'graph minzoom has been used instead');
} else if (maxZoom && ratio > maxZoom) {
realRatio = minZoom;
console.warn('fitview failed, ratio out of range, ratio: %f', ratio, 'graph maxzoom has been used instead');
}
let zoomedMatrix = transform(translatedMatrix, [
['t', -viewCenter.x, -viewCenter.y],
['s', realRatio, realRatio],
['t', viewCenter.x, viewCenter.y],
]);
// Animation
const animationConfig = getAnimateCfgWithCallback({
animateCfg,
callback: () => {
graph.emit('viewportchange', { action: 'translate', matrix: translatedMatrix });
graph.emit('viewportchange', { action: 'zoom', matrix: zoomedMatrix });
}
});
group.animate((ratio: number) => {
return { matrix: lerpArray(startMatrix, zoomedMatrix, ratio) };
}, animationConfig);
}
// fit view graph
public fitView(animate?: boolean, animateCfg?: GraphAnimateConfig) {
const { graph } = this;
@ -51,6 +95,7 @@ export default class ViewController {
const width: number = graph.get('width');
const height: number = graph.get('height');
const group: IGroup = graph.get('group');
const startMatrix = group.getMatrix() || [1, 0, 0, 0, 1, 0, 0, 0, 1];
group.resetMatrix();
const bbox = group.getCanvasBBox();
@ -62,29 +107,22 @@ export default class ViewController {
y: bbox.y + bbox.height / 2,
};
let animateConfig = animateCfg;
if (animate) {
animateConfig = {
...(animateCfg || {
duration: 500,
easing: 'easeCubic'
}),
callback: () => {
graph.zoom(ratio, viewCenter, true, animateCfg);
animateCfg?.callback?.();
}
}
}
// Compute ratio
const w = (width - padding[1] - padding[3]) / bbox.width;
const h = (height - padding[0] - padding[2]) / bbox.height;
let ratio = w;
if (w > h) {
ratio = h;
}
graph.translate(viewCenter.x - groupCenter.x, viewCenter.y - groupCenter.y, animate, animateConfig);
if (!animate && !graph.zoom(ratio, viewCenter)) {
console.warn('zoom failed, ratio out of range, ratio: %f', ratio);
if (animate) {
this.animatedFitView(group, startMatrix, animateCfg, bbox, viewCenter, groupCenter, ratio);
} else {
graph.translate(viewCenter.x - groupCenter.x, viewCenter.y - groupCenter.y);
if (!graph.zoom(ratio, viewCenter)) {
console.warn('zoom failed, ratio out of range, ratio: %f', ratio);
}
}
}
@ -100,6 +138,7 @@ export default class ViewController {
const width: number = graph.get('width');
const height: number = graph.get('height');
const group: IGroup = graph.get('group');
const startMatrix = group.getMatrix() || [1, 0, 0, 0, 1, 0, 0, 0, 1];
group.resetMatrix();
const bbox = group.getCanvasBBox();
@ -111,7 +150,7 @@ export default class ViewController {
y: bbox.y + bbox.height / 2,
};
graph.translate(viewCenter.x - groupCenter.x, viewCenter.y - groupCenter.y, animate, animateCfg);
// Compute ratio
const wRatio = (width - padding[1] - padding[3]) / bbox.width;
const hRatio = (height - padding[0] - padding[2]) / bbox.height;
let ratio;
@ -128,15 +167,21 @@ export default class ViewController {
ratio = ratio < 1 ? ratio : 1;
}
const initZoomRatio = graph.getZoom();
let endZoom = initZoomRatio * ratio;
const minZoom = graph.get('minZoom');
// 如果zoom小于最小zoom, 则以最小zoom为准
if (endZoom < minZoom) {
endZoom = minZoom;
console.warn('fitview failed, ratio out of range, ratio: %f', ratio, 'graph minzoom has been used instead');
if (animate) {
this.animatedFitView(group, startMatrix, animateCfg, bbox, viewCenter, groupCenter, ratio);
} else {
const initZoomRatio = graph.getZoom();
let endZoom = initZoomRatio * ratio;
const minZoom = graph.get('minZoom');
// 如果zoom小于最小zoom, 则以最小zoom为准
if (endZoom < minZoom) {
endZoom = minZoom;
console.warn('fitview failed, ratio out of range, ratio: %f', ratio, 'graph minzoom has been used instead');
}
graph.translate(viewCenter.x - groupCenter.x, viewCenter.y - groupCenter.y);
graph.zoomTo(endZoom, viewCenter);
}
graph.zoomTo(endZoom, viewCenter, animate, animateCfg);
}
public getFormatPadding(): number[] {

View File

@ -37,7 +37,7 @@ import { lerp, move } from '../util/math';
import { dataValidation, singleDataValidation } from '../util/validation';
import Global from '../global';
import { ItemController, ModeController, StateController, ViewController } from './controller';
import { plainCombosToTrees, traverseTree, reconstructTree, traverseTreeUp } from '../util/graphic';
import { plainCombosToTrees, traverseTree, reconstructTree, traverseTreeUp, getAnimateCfgWithCallback } from '../util/graphic';
import Hull from '../item/hull';
const { transform } = ext;
@ -224,7 +224,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
/**
* Minimum scale size
*/
minZoom: 0.2,
minZoom: 0.02,
/**
* Maxmum scale size
*/
@ -553,36 +553,12 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
* @param {string} state
* @return {object}
*/
public findAllByState<T extends Item>(type: ITEM_TYPE, state: string): T[] {
return this.findAll(type, item => item.hasState(state));
}
private getAnimateCfgWithCallback({
animateCfg,
callback
}: {
animateCfg: GraphAnimateConfig;
callback: () => void;
}): GraphAnimateConfig {
let animateConfig: GraphAnimateConfig;
if (!animateCfg) {
animateConfig = {
duration: 500,
callback
};
public findAllByState<T extends Item>(type: ITEM_TYPE, state: string, additionalFilter?: (item: Item) => boolean): T[] {
if (additionalFilter) {
return this.findAll(type, item => item.hasState(state) && additionalFilter(item));
} else {
animateConfig = clone(animateCfg);
if (animateCfg.callback) {
const animateCfgCallback = animateCfg.callback;
animateConfig.callback = () => {
callback();
animateCfgCallback();
}
} else {
animateConfig.callback = callback;
}
return this.findAll(type, item => item.hasState(state));
}
return animateConfig;
}
/**
@ -600,7 +576,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1];
}
if (animate) {
const animateConfig = this.getAnimateCfgWithCallback({
const animateConfig = getAnimateCfgWithCallback({
animateCfg,
callback: () => this.emit('viewportchange', { action: 'translate', matrix: group.getMatrix() })
});
@ -728,29 +704,32 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
*/
public zoom(ratio: number, center?: Point, animate?: boolean, animateCfg?: GraphAnimateConfig): boolean {
const group: IGroup = this.get('group');
let matrix = clone(group.getMatrix());
let matrix = clone(group.getMatrix()) || [1, 0, 0, 0, 1, 0, 0, 0, 1];
const minZoom: number = this.get('minZoom');
const maxZoom: number = this.get('maxZoom');
const currentZoom = this.getZoom() || 1;
const targetZoom = currentZoom * ratio;
let finalRatio = ratio;
if (!matrix) {
matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1];
let failed = false;
if (minZoom && targetZoom < minZoom) {
finalRatio = minZoom / currentZoom;
failed = true;
} else if (maxZoom && targetZoom > maxZoom) {
finalRatio = maxZoom / currentZoom;
failed = true;
}
if (center) {
matrix = transform(matrix, [
['t', -center.x, -center.y],
['s', ratio, ratio],
['s', finalRatio, finalRatio],
['t', center.x, center.y],
]);
} else {
matrix = transform(matrix, [['s', ratio, ratio]]);
matrix = transform(matrix, [['s', finalRatio, finalRatio]]);
}
if ((minZoom && matrix[0] < minZoom) || (maxZoom && matrix[0] > maxZoom)) {
return false;
}
// matrix = [2, 0, 0, 0, 2, 0, -125, -125, 1];
if (animate) {
// Clone the original matrix to perform the animation
let aniMatrix = clone(group.getMatrix());
@ -758,9 +737,9 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
aniMatrix = [1, 0, 0, 0, 1, 0, 0, 0, 1];
}
const initialRatio = aniMatrix[0];
const targetRatio = initialRatio * ratio;
const targetRatio = initialRatio * finalRatio;
const animateConfig = this.getAnimateCfgWithCallback({
const animateConfig = getAnimateCfgWithCallback({
animateCfg,
callback: () => this.emit('viewportchange', { action: 'zoom', matrix: group.getMatrix() })
});
@ -785,7 +764,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
this.autoPaint();
}
return true;
return !failed;
}
/**
@ -2459,7 +2438,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
* cfg type String
* cfg type
*/
public updateLayout(cfg: any, align?: 'center' | 'begin', alignPoint?: IPoint): void {
public updateLayout(cfg: any, align?: 'center' | 'begin', alignPoint?: IPoint, stack: boolean = true): void {
const layoutController = this.get('layoutController');
if (isString(cfg)) {
@ -2501,7 +2480,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
}
}
const oriLayoutCfg = this.get('layout');
const oriLayoutCfg = { ...this.get('layout') };
const layoutCfg: any = {};
Object.assign(layoutCfg, oriLayoutCfg, cfg);
this.set('layout', layoutCfg);
@ -2518,6 +2497,10 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
// has different type, change layout
layoutController.changeLayout(layoutCfg);
}
if (stack && this.get('enabledStack')) {
this.pushStack('layout', { before: oriLayoutCfg, after: layoutCfg });
}
}
/**

View File

@ -500,7 +500,7 @@ export interface IAbstractGraph extends EventEmitter {
* @param {string} state z状态
* @return {object}
*/
findAllByState: <T extends Item>(type: ITEM_TYPE, state: string) => T[];
findAllByState: <T extends Item>(type: ITEM_TYPE, state: string, additionalFilter?: (item: Item) => boolean) => T[];
/**
*
@ -508,7 +508,7 @@ export interface IAbstractGraph extends EventEmitter {
* cfg type String
* cfg type
*/
updateLayout: (cfg: LayoutConfig, align?: 'center' | 'begin', canvasPoint?: IPoint) => void;
updateLayout: (cfg: LayoutConfig, align?: 'center' | 'begin', canvasPoint?: IPoint, stack?: boolean) => void;
/**
*

View File

@ -96,11 +96,13 @@ export default class ItemBase implements IItemBase {
const itemType = this.get('type');
if (!id) {
if (typeof id === 'undefined') {
id = uniqueId(itemType);
this.get('model').id = id;
} else if (typeof id !== 'string') {
id = String(id);
}
this.get('model').id = id;
this.set('id', id);
const { group } = cfg;
if (group) {

View File

@ -10,7 +10,8 @@ import {
NodeConfig,
ComboTree,
ComboConfig,
ICombo
ICombo,
GraphAnimateConfig
} from '../types';
import { applyMatrix } from './math';
import letterAspectRatio from './letterAspectRatio';
@ -686,3 +687,31 @@ export const cloneBesidesImg = (obj) => {
});
return clonedObj;
}
export const getAnimateCfgWithCallback = ({
animateCfg,
callback
}: {
animateCfg: GraphAnimateConfig;
callback: () => void;
}): GraphAnimateConfig => {
let animateConfig: GraphAnimateConfig;
if (!animateCfg) {
animateConfig = {
duration: 500,
callback
};
} else {
animateConfig = clone(animateCfg);
if (animateCfg.callback) {
const animateCfgCallback = animateCfg.callback;
animateConfig.callback = () => {
callback();
animateCfgCallback();
}
} else {
animateConfig.callback = callback;
}
}
return animateConfig;
}

View File

@ -823,11 +823,29 @@ export const pointLineDistance = (line, point) => {
/**
* Linearly interpolate between start and end, where alpha is the percent distance along the line.
* alpha = 0 will be start, and alpha = 1 will be end.
* @param {Number} start
* @param {Number} end
* @param {Number} alpha interpolation factor, typically in the closed interval [0, 1]
* @returns
* @param {number} start
* @param {number} end
* @param {number} alpha interpolation factor, typically in the closed interval [0, 1]
* @returns {number}
*/
export const lerp = (start: number, end: number, alpha: number): number => {
return start + (end - start) * alpha;
}
/**
* Linearly interpolate between start and end arrays, where alpha is the percent distance along the line.
* alpha = 0 will be start, and alpha = 1 will be end.
* @param {number[]} start
* @param {number[]} end
* @param {number} alpha interpolation factor, typically in the closed interval [0, 1]
* @returns {number[]}
*/
export const lerpArray = (start: number[], end: number[], alpha: number): number[] => {
var len = Math.min(start.length, end.length);
const out = new Array(len);
for (var i = 0; i < len; i++) {
out[i] = lerp(start[i], end[i], alpha);
}
return out;
}

View File

@ -221,11 +221,11 @@ describe('graph', () => {
graph.zoom(0.5, { x: 100, y: 100 });
matrix = graph.get('group').getMatrix();
expect(matrix).toBe(null);
expect(JSON.stringify(matrix)).toBe(JSON.stringify([2, 0, 0, 0, 2, 0, -100, -100, 1])); // 使用最小值
graph.zoom(5.5);
matrix = graph.get('group').getMatrix();
expect(matrix).toBe(null);
expect(JSON.stringify(matrix)).toBe(JSON.stringify([5, 0, 0, 0, 5, 0, -250, -250, 1])); // 使用最大值
});
it('zoomTo', () => {
@ -529,6 +529,30 @@ describe('graph', () => {
expect(nodes[0]).toEqual(node2);
});
it('findAllByState should not select nodes that are not visible', () => {
globalGraph.clear();
const node1 = globalGraph.addItem('node', {
id: 'node1',
visible: false
});
const node2 = globalGraph.addItem('node', {
id: 'node2'
});
node1.setState('selected', true);
node2.setState('selected', true);
function additionalFilter(item) {
return item.isVisible();
}
const selectedNodes = globalGraph.findAllByState('node', 'selected', additionalFilter);
expect(selectedNodes.length).toEqual(1);
});
it('refresh positions', () => {
const data = { id: 'node4', x: 100, y: 50, size: 50, className: 'test test2' };
const node = globalGraph.addItem('node', data);

View File

@ -16,9 +16,9 @@ class Graph extends AbstractGraph {
super(cfg);
}
initEventController() {}
initEventController() { }
initLayoutController() {}
initLayoutController() { }
initCanvas() {
let container: string | HTMLElement | Element | null = this.get('container');
@ -48,7 +48,7 @@ class Graph extends AbstractGraph {
this.set('canvas', canvas);
}
initPlugins() {}
initPlugins() { }
}
describe('graph', () => {
@ -363,11 +363,11 @@ describe('graph', () => {
graph.zoom(0.5, { x: 100, y: 100 });
matrix = graph.get('group').getMatrix();
expect(matrix).toBe(null);
expect(JSON.stringify(matrix)).toBe(JSON.stringify([2, 0, 0, 0, 2, 0, -100, -100, 1])); // 使用最小值
graph.zoom(5.5);
matrix = graph.get('group').getMatrix();
expect(matrix).toBe(null);
expect(JSON.stringify(matrix)).toBe(JSON.stringify([5, 0, 0, 0, 5, 0, -250, -250, 1])); // 使用最大值
graph.destroy();
});

View File

@ -1,6 +1,6 @@
{
"name": "@antv/g6-element",
"version": "0.6.15",
"version": "0.6.16",
"description": "A Graph Visualization Framework in JavaScript",
"keywords": [
"antv",
@ -61,7 +61,7 @@
},
"dependencies": {
"@antv/g-base": "^0.5.1",
"@antv/g6-core": "0.6.15",
"@antv/g6-core": "0.6.16",
"@antv/util": "~2.0.5"
},
"devDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@antv/g6",
"version": "4.6.15",
"version": "4.6.16",
"description": "A Graph Visualization Framework in JavaScript",
"keywords": [
"antv",
@ -66,7 +66,7 @@
]
},
"dependencies": {
"@antv/g6-pc": "0.6.15"
"@antv/g6-pc": "0.6.16"
},
"devDependencies": {
"@babel/core": "^7.7.7",

View File

@ -1,7 +1,7 @@
import G6 from '@antv/g6-pc';
G6.version = '4.6.15';
G6.version = '4.6.16';
export * from '@antv/g6-pc';
export default G6;
export const version = '4.6.15';
export const version = '4.6.16';

View File

@ -1,6 +1,6 @@
{
"name": "@antv/g6-pc",
"version": "0.6.15",
"version": "0.6.16",
"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.6.15",
"@antv/g6-element": "0.6.15",
"@antv/g6-plugin": "0.6.15",
"@antv/g6-core": "0.6.16",
"@antv/g6-element": "0.6.16",
"@antv/g6-plugin": "0.6.16",
"@antv/hierarchy": "^0.6.7",
"@antv/layout": "^0.2.5",
"@antv/matrix-util": "^3.1.0-beta.3",

View File

@ -217,6 +217,7 @@ export default {
if (!shapes) continue;
shapes.forEach((shape) => {
const oriVis = shape.get('ori-visibility');
shape.set('ori-visibility', undefined);
if (oriVis) shape.show();
});
}
@ -229,6 +230,7 @@ export default {
const isKeyShape = child.get('isKeyShape');
if (!isKeyShape) {
const oriVis = child.get('ori-visibility');
child.set('ori-visibility', undefined);
if (oriVis) child.show();
}
}

View File

@ -5,7 +5,7 @@
*/
import { each } from '@antv/util';
import { IGroup } from '@antv/g-base';
import { G6Event, IG6GraphEvent, Item, ComboConfig, ICombo, INode } from '@antv/g6-core';
import { G6Event, IG6GraphEvent, Item, ComboConfig, ICombo, INode, IEdge } from '@antv/g6-core';
import { IGraph } from '../interface/graph';
import Util from '../util';
import Global from '../global';
@ -100,6 +100,13 @@ export default {
this.targets = combos;
}
const beforeDragItems = [];
this.targets.forEach(t => {
const { x, y, id } = t.getModel();
beforeDragItems.push({ x, y, id });
});
this.set('beforeDragItems', beforeDragItems);
if (this.activeState) {
this.targets.map((combo: ICombo) => {
const model = combo.getModel() as ComboConfig;
@ -191,6 +198,7 @@ export default {
updatePositions(evt: IG6GraphEvent, restore: boolean) {
// 当启用 delegate 时,拖动结束时需要更新 combo
if (this.enableDelegate || restore) {
console.log('updatePositions', this.targets);
each(this.targets, (item) => {
this.updateCombo(item, evt, restore);
});
@ -334,12 +342,21 @@ export default {
// 若没有被放置的 combo则是被放置在画布上
if (!comboDropedOn) {
const stack = graph.get('enabledStack') && this.enableStack;
const stackData = {
before: { nodes: [], edges: [], combos: [].concat(this.get('beforeDragItems')) },
after: { nodes: [], edges: [], combos: [] },
};
this.targets.map((combo: ICombo) => {
// 将 Combo 放置到某个 Combo 上面时,只有当 onlyChangeComboSize 为 false 时候才更新 Combo 结构
if (!this.onlyChangeComboSize) {
graph.updateComboTree(combo, undefined, stack);
} else {
graph.updateCombo(combo);
const { x, y, id } = combo.getModel();
stackData.after.combos.push({ x, y, id });
graph.pushStack('update', stackData);
}
});
}
@ -355,32 +372,35 @@ export default {
* @param data
* @param fn
*/
traverse<T extends Item>(data: T, fn: (param: T) => boolean) {
if (fn(data) === false) {
traverse<T extends Item>(data: T, fn: (param: T, cacheMap) => boolean, edgesToBeUpdate = {}) {
if (fn(data, edgesToBeUpdate) === false) {
return;
}
if (data) {
const combos = data.get('combos');
each(combos, (child) => {
this.traverse(child, fn);
this.traverse(child, fn, edgesToBeUpdate);
});
const nodes = data.get('nodes');
each(nodes, (child) => {
this.traverse(child, fn);
this.traverse(child, fn, edgesToBeUpdate);
});
}
},
updateCombo(item: ICombo, evt: IG6GraphEvent, restore: boolean) {
this.traverse(item, (param) => {
if (param.destroyed) {
this.updateSingleItem(item, evt, restore);
const edgesToBeUpdate: { [id: string]: IEdge } = {};
this.traverse(item, (paramItem, paramEdgesMap) => {
if (paramItem.destroyed) {
return false;
}
this.updateSingleItem(param, evt, restore);
paramItem.getEdges().forEach(edge => paramEdgesMap[edge.getID()] = edge);
return true;
});
}, edgesToBeUpdate);
Object.values(edgesToBeUpdate).forEach(edge => edge.refresh());
},
/**
@ -404,13 +424,14 @@ export default {
let x: number = evt.x - origin.x + this.point[itemId].x;
let y: number = evt.y - origin.y + this.point[itemId].y;
console.log('restore', restore);
if (restore) {
x += origin.x - evt.x;
y += origin.y - evt.y;
}
graph.updateItem(item, { x, y }, false);
item.getEdges()?.forEach(edge => edge.refresh());
// item.getEdges()?.forEach(edge => edge.refresh());
},
/**

View File

@ -154,7 +154,8 @@ export default {
}
const beforeDragNodes = [];
this.targets.forEach(t => {
beforeDragNodes.push(clone(t.getModel()));
const { x, y, id } = t.getModel();
beforeDragNodes.push({ x, y, id });
});
this.set('beforeDragNodes', beforeDragNodes);
@ -252,20 +253,12 @@ export default {
};
this.get('beforeDragNodes').forEach(model => {
stackData.before.nodes.push({
id: model.id,
x: model.x,
y: model.y,
});
stackData.before.nodes.push(model);
});
this.targets.forEach(target => {
const targetModel = target.getModel();
stackData.after.nodes.push({
id: targetModel.id,
x: targetModel.x,
y: targetModel.y,
});
const { x, y, id } = target.getModel();
stackData.after.nodes.push({ x, y, id });
});
graph.pushStack('update', clone(stackData));
}

View File

@ -169,7 +169,6 @@ export default {
ratio = 1 / (1 - DELTA * sensitivity);
}
zoom = graphZoom * ratio;
// const zoom = ratio * graphZoom;
const minZoom = this.get('minZoom') || graph.get('minZoom');
const maxZoom = this.get('maxZoom') || graph.get('maxZoom');
if (zoom > maxZoom) {
@ -233,11 +232,13 @@ export default {
if (currentZoom < optimizeZoom) {
const keyShape = node.getKeyShape();
const oriVis = keyShape.get('ori-visibility');
keyShape.set('ori-visibility', undefined);
if (oriVis) keyShape.show();
} else {
for (let c = 0; c < childrenLength; c++) {
const shape = children[c];
const oriVis = shape.get('ori-visibility');
shape.set('ori-visibility', undefined);
if (!shape.get('visible') && oriVis) {
if (oriVis) shape.show();
}
@ -252,12 +253,14 @@ export default {
if (currentZoom < optimizeZoom) {
const keyShape = edge.getKeyShape();
const oriVis = keyShape.get('ori-visibility');
keyShape.set('ori-visibility', undefined);
if (oriVis) keyShape.show();
} else {
for (let c = 0; c < childrenLength; c++) {
const shape = children[c];
if (!shape.get('visible')) {
const oriVis = shape.get('ori-visibility');
shape.set('ori-visibility', undefined);
if (oriVis) shape.show();
}
}

View File

@ -7,7 +7,7 @@ const textColor = 'rgb(0, 0, 0)';
const colorSet = getColorsWithSubjectColor(subjectColor, backColor);
export default {
version: '0.6.15',
version: '0.6.16',
rootContainerClassName: 'root-container',
nodeContainerClassName: 'node-container',
edgeContainerClassName: 'edge-container',

View File

@ -27,8 +27,8 @@ describe('shortcuts-call', () => {
graph.emit('keydown', { key: '1' });
graph.emit('keyup');
const matrix = graph.getGroup().getMatrix();
expect(matrix[6]).toBe(200);
expect(matrix[7]).toBe(200);
expect(matrix[6]).toBe(-250);
expect(matrix[7]).toBe(-250);
graph.destroy();
});

View File

@ -0,0 +1,87 @@
import { ToolBar } from '@antv/g6-plugin';
import insertCss from 'insert-css';
import G6, { Tooltip } from '../../src';
const div = document.createElement('div');
div.id = 'container';
document.body.appendChild(div);
const data = {
nodes: [
{ id: 'node1', x: 350, y: 200, comboId: 'combo1' },
{ id: 'node2', x: 350, y: 250, comboId: 'combo1' },
{ id: 'node3', x: 100, y: 200, comboId: 'combo3' },
],
edges: [
{ source: 'node1', target: 'node2' },
{ source: 'node1', target: 'node3' },
{ source: 'combo1', target: 'node3' },
],
combos: [
{ id: 'combo1', label: 'Combo 1', parentId: 'combo2' },
{ id: 'combo2', label: 'Combo 2' },
{ id: 'combo3', label: 'Combo 3', collapsed: true },
],
};
describe('combo states', () => {
it('combo state bug', () => {
const graph = new G6.Graph({
container: 'container',
width: 500,
height: 500,
// Set groupByTypes to false to get rendering result with reasonable visual zIndex for combos
groupByTypes: false,
defaultCombo: {
type: 'circle',
style: {
lineWidth: 1,
},
labelCfg: {
refY: 15,
position: 'bottom',
},
},
nodeStateStyles: {
hover: {
fill: 'red',
},
},
comboStateStyles: {
hover: {
stroke: 'green',
},
selected: {
stroke: 'red',
},
},
modes: {
default: ['drag-canvas', 'drag-node', 'drag-combo', 'collapse-expand-combo', 'activate-relations', 'shortcuts-call'],
},
});
graph.data(data);
graph.render();
// graph.on('combo:mouseenter', (evt) => {
// graph.setItemState(evt.item, 'hover', true);
// });
// graph.on('combo:mouseleave', (evt) => {
// graph.setItemState(evt.item, 'hover', false);
// });
// // combo 设置不存在的 state
// graph.on('combo:click', (evt) => {
// graph.setItemState(evt.item, 'notFound', true);
// });
// graph.on('node:mouseenter', (evt) => {
// graph.setItemState(evt.item, 'hover', true);
// });
// graph.on('node:mouseleave', (evt) => {
// graph.setItemState(evt.item, 'hover', false);
// });
});
});

View File

@ -245,11 +245,11 @@ describe('graph', () => {
graph.zoom(0.5, { x: 100, y: 100 });
matrix = graph.get('group').getMatrix();
expect(matrix).toBe(null);
expect(JSON.stringify(matrix)).toBe(JSON.stringify([2, 0, 0, 0, 2, 0, -100, -100, 1])); // 使用最小值
graph.zoom(5.5);
matrix = graph.get('group').getMatrix();
expect(matrix).toBe(null);
expect(JSON.stringify(matrix)).toBe(JSON.stringify([5, 0, 0, 0, 5, 0, -250, -250, 1])); // 使用最大值
});
it('zoomTo', () => {

View File

@ -329,11 +329,11 @@ describe('graph', () => {
graph.zoom(0.5, { x: 100, y: 100 });
matrix = graph.get('group').getMatrix();
expect(matrix).toBe(null);
expect(JSON.stringify(matrix)).toBe(JSON.stringify([2, 0, 0, 0, 2, 0, -100, -100, 1])); // 使用最大值
graph.zoom(5.5);
matrix = graph.get('group').getMatrix();
expect(matrix).toBe(null);
expect(JSON.stringify(matrix)).toBe(JSON.stringify([5, 0, 0, 0, 5, 0, -250, -250, 1])); // 使用最大值
graph.destroy();
});

View File

@ -1,6 +1,6 @@
{
"name": "@antv/g6-plugin",
"version": "0.6.15",
"version": "0.6.16",
"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.6.15",
"@antv/g6-element": "0.6.15",
"@antv/g6-core": "0.6.16",
"@antv/g6-element": "0.6.16",
"@antv/matrix-util": "^3.1.0-beta.3",
"@antv/scale": "^0.3.4",
"@antv/util": "^2.0.9",

View File

@ -1,7 +1,7 @@
import { modifyCSS, createDom } from '@antv/dom-util';
import { clone, isString } from '@antv/util';
import Base, { IPluginBaseConfig } from '../base';
import { IAbstractGraph as IGraph } from '@antv/g6-core';
import { IAbstractGraph as IGraph, ICombo } from '@antv/g6-core';
import { Point } from '@antv/g-base';
import insertCss from 'insert-css';
@ -74,7 +74,7 @@ const getEventPath = (evt: MouseEvent) => {
};
export default class ToolBar extends Base {
constructor (config?: ToolBarConfig) {
constructor(config?: ToolBarConfig) {
super(config);
}
public getDefaultCfgs(): ToolBarConfig {
@ -256,7 +256,9 @@ export default class ToolBar extends Base {
const array = data[key];
if (!array) return;
array.forEach((model) => {
graph.updateItem(model.id, model, false);
const item = graph.findById(model.id);
graph.updateItem(item, model, false);
if (item.getType() === 'combo') graph.updateCombo(item as ICombo)
});
});
break;
@ -293,6 +295,9 @@ export default class ToolBar extends Base {
});
});
break;
case 'layout':
graph.updateLayout(data, undefined, undefined, false);
break;
default:
}
}
@ -342,7 +347,9 @@ export default class ToolBar extends Base {
const array = data[key];
if (!array) return;
array.forEach((model) => {
graph.updateItem(model.id, model, false);
const item = graph.findById(model.id);
graph.updateItem(item, model, false);
if (item.getType() === 'combo') graph.updateCombo(item as ICombo)
});
});
break;
@ -387,6 +394,9 @@ export default class ToolBar extends Base {
});
});
break;
case 'layout':
graph.updateLayout(data, undefined, undefined, false);
break;
default:
}
}

View File

@ -217,7 +217,6 @@ export default class Tooltip extends Base {
const offsetX = this.get('offsetX') || 0;
const offsetY = this.get('offsetY') || 0;
// const mousePos = graph.getPointByClient(e.clientX, e.clientY);
let point = graph.getPointByClient(e.clientX, e.clientY);
const fixToNode = this.get('fixToNode');

View File

@ -31,7 +31,7 @@ const ReactNode = ({ cfg = {} }) => {
cursor: 'move',
stroke: cfg.color,
}}
draggable="true"
draggable
>
<Text
style={{

View File

@ -29,7 +29,7 @@ const ReactNode = ({ cfg = {} }) => {
cursor: 'move',
stroke: cfg.color,
}}
draggable="true"
draggable
>
<Text
style={{
@ -114,7 +114,7 @@ const ReactNode = ({ cfg = {} }) => {
cursor: 'move',
stroke: cfg.color,
}}
draggable="true"
draggable
>
<Text
style={{

View File

@ -28,9 +28,9 @@ const ReactNode = ({ cfg = {} }) => {
radius: [6, 6, 0, 0],
cursor: 'move',
stroke: cfg.color,
justyfyContent: 'center',
justifyContent: 'center',
}}
draggable="true"
draggable
>
<Text
style={{
@ -111,9 +111,9 @@ const ReactNode = ({ cfg = {} }) => {
radius: [6, 6, 0, 0],
cursor: 'move',
stroke: cfg.color,
justyfyContent: 'center',
justifyContent: 'center',
}}
draggable="true"
draggable
>
<Text
style={{

View File

@ -20,7 +20,7 @@ const ReactNode = ({ cfg = {} }) => {
cursor: 'move',
stroke: cfg.color,
}}
draggable="true"
draggable
/>
<Rect

View File

@ -20,7 +20,7 @@ const ReactNode = ({ cfg = {} }) => {
cursor: 'move',
stroke: cfg.color,
}}
draggable="true"
draggable
/>
<Rect

View File

@ -38,6 +38,11 @@ interface GroupProps {
* @description.zh-CN
*/
animation?: Partial<AnimationConfig>;
/**
* @description.en-US Nodes wrapped within the component
* @description.zh-CN
*/
children?: React.ReactNode;
}
export type CommonProps = GroupProps & EventAttrs;

View File

@ -24,7 +24,7 @@ interface RectProps extends CommonProps {
/**
* @description.en-US style of shape
*/
style: RectStyle;
style?: RectStyle;
}
const Rect: React.FC<RectProps> = (props) => {

View File

@ -48,7 +48,7 @@ interface TextProps extends CommonProps {
/**
* @description.en-US style of shape
*/
style: TextStyle;
style?: TextStyle;
}
const Text: React.FC<TextProps> = (props) => {

View File

@ -169,15 +169,15 @@ The easing function name of animation. Please refer to ease in d3.
### GraphOptions.minZoom
<description> _Number_ **optional** _default:_ `0.2`</description>
<description> _Number_ **optional** _default:_ `0.02`</description>
The minimum zoom ratio.
The minimum zoom ratio. If the ratio to be scaled in `fitView`, `zoom`, or `zoomTo` is smaller than the minZoom, the minZoom will takes effect and the current funcion will return false.
### GraphOptions.maxZoom
<description> _Number_ **optional** _default:_ `10`</description>
The maximum zoom ratio.
The maximum zoom ratio. If the ratio to be scaled in `fitView`, `zoom`, or `zoomTo` is bigger than the maxZoom, the maxZoom will takes effect and the current funcion will return false.
### GraphOptions.layout

View File

@ -169,15 +169,15 @@ const graph = new G6.Graph({
### GraphOptions.minZoom
<description> _Number_ **optional** _default:_ `0.2`</description>
<description> _Number_ **optional** _default:_ `0.02`</description>
最小缩放比例。
最小缩放比例。若 fitView、zoom、zoomTo 等操作导致图的缩放比例小于该值,则将使用该值进行缩放,并返回 false。
### GraphOptions.maxZoom
<description> _Number_ **optional** _default:_ `10`</description>
最大缩放比例。
最大缩放比例。若 fitView、zoom、zoomTo 等操作导致图的缩放比例大于该值,则将使用该值进行缩放,并返回 false。
### GraphOptions.layout

View File

@ -1,7 +1,7 @@
{
"private": true,
"name": "@antv/g6-site",
"version": "4.6.15",
"version": "4.6.16",
"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.6.15",
"@antv/g6": "4.6.16",
"@antv/gatsby-theme-antv": "1.1.15",
"@antv/util": "^2.0.9",
"@antv/vis-predict-engine": "^0.1.1",