Skip to content

Commit

Permalink
fix: wrong dropped position for drag-combo with enableDelegate, close… (
Browse files Browse the repository at this point in the history
#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 <[email protected]>
Co-authored-by: femiabdul <[email protected]>
Co-authored-by: MikalaiLappo <[email protected]>
Co-authored-by: Fabio Tacchelli <[email protected]>
  • Loading branch information
5 people authored Aug 1, 2022
1 parent 5a7681c commit 5407845
Show file tree
Hide file tree
Showing 38 changed files with 392 additions and 160 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
103 changes: 74 additions & 29 deletions packages/core/src/graph/controller/view.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -44,13 +45,57 @@ 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;
const padding = this.getFormatPadding();
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();

Expand All @@ -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);
}
}
}

Expand All @@ -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();

Expand All @@ -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;
Expand All @@ -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[] {
Expand Down
75 changes: 29 additions & 46 deletions packages/core/src/graph/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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;
}

/**
Expand All @@ -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() })
});
Expand Down Expand Up @@ -728,28 +704,31 @@ 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]]);
}

if ((minZoom && matrix[0] < minZoom) || (maxZoom && matrix[0] > maxZoom)) {
return false;
matrix = transform(matrix, [['s', finalRatio, finalRatio]]);
}
// matrix = [2, 0, 0, 0, 2, 0, -125, -125, 1];

if (animate) {
// Clone the original matrix to perform the animation
Expand All @@ -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() })
});
Expand All @@ -785,7 +764,7 @@ export default abstract class AbstractGraph extends EventEmitter implements IAbs
this.autoPaint();
}

return true;
return !failed;
}

/**
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);
Expand All @@ -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 });
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/interface/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,15 +500,15 @@ 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[];

/**
* 更换布局配置项
* @param {object} cfg 新布局配置项
* 若 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;

/**
* 重新以当前示例中配置的属性进行一次布局
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/item/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit 5407845

Please sign in to comment.