feat: add autoResize option on graph (#5362)

* feat: add autoResize option on graph

* test: add test cases
This commit is contained in:
hustcc 2024-01-24 12:37:03 +08:00 committed by GitHub
parent 08de942823
commit bb847876c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 211 additions and 1 deletions

View File

@ -5,6 +5,7 @@ import { GraphChange, ID } from '@antv/graphlib';
import {
clone,
createDOM,
debounce,
groupBy,
isArray,
isEmpty,
@ -35,6 +36,7 @@ import { FitViewRules, GraphTransformOptions } from '../types/view';
import { getCombinedCanvasesBounds } from '../utils/bbox';
import { changeRenderer, createCanvas } from '../utils/canvas';
import { cloneJSON, isEmptyGraph } from '../utils/data';
import { sizeOf } from '../utils/dom';
import { error, warn } from '../utils/invariant';
import { getLayoutBounds } from '../utils/layout';
import { formatPadding } from '../utils/shape';
@ -79,7 +81,7 @@ export class Graph<B extends BehaviorRegistry = any, T extends ThemeSolverRegist
private themeController: ThemeController;
private pluginController: PluginController;
private defaultSpecification = {
private defaultSpecification: Specification = {
theme: {
type: 'spec',
base: 'light',
@ -109,6 +111,9 @@ export class Graph<B extends BehaviorRegistry = any, T extends ThemeSolverRegist
// TODO: handle multiple type data configs
this.read(data as GraphData);
}
// Listening window.resize to autoResize.
this.specification.autoResize && window.addEventListener('resize', this.onResize);
}
/**
@ -2530,6 +2535,16 @@ export class Graph<B extends BehaviorRegistry = any, T extends ThemeSolverRegist
// this.themeController.destroy();
// this.itemController.destroy();
window.removeEventListener('resize', this.onResize);
this.destroyed = true;
}
private onResize = debounce(() => {
const { width, height } = this.specification;
const [w, h] = sizeOf(this.container);
if (width !== w || height !== h) {
this.setSize([w, h]);
}
}, 300);
}

View File

@ -26,6 +26,10 @@ export interface Specification<B extends BehaviorRegistry, T extends ThemeSolver
transientLabelCanvas?: Canvas;
width?: number;
height?: number;
/**
* Auto resize canvas by listening `resize` event. Default is False.
*/
autoResize?: boolean;
renderer?:
| RendererName
| {

View File

@ -0,0 +1,44 @@
import { isNumber } from '@antv/util';
const parseInt10 = (d: string) => (d ? parseInt(d) : 0);
/**
* Get the container's bounding size.
* @param container dom element.
* @returns the container width and height
*/
function getContainerSize(container: HTMLElement): [number, number] {
const style = getComputedStyle(container);
const wrapperWidth = container.clientWidth || parseInt10(style.width);
const wrapperHeight = container.clientHeight || parseInt10(style.height);
const widthPadding = parseInt10(style.paddingLeft) + parseInt10(style.paddingRight);
const heightPadding = parseInt10(style.paddingTop) + parseInt10(style.paddingBottom);
return [wrapperWidth - widthPadding, wrapperHeight - heightPadding];
}
/**
* Get the size of Graph.
* @param container container of Graph
* @returns Size of Graph
*/
export function sizeOf(container: HTMLElement): [number, number] {
let effectiveWidth = 640;
let effectiveHeight = 480;
const [containerWidth, containerHeight] = getContainerSize(container);
effectiveWidth = containerWidth || effectiveWidth;
effectiveHeight = containerHeight || effectiveHeight;
/** Minimum width */
const MIN_CHART_WIDTH = 1;
/** Minimum height */
const MIN_CHART_HEIGHT = 1;
return [
Math.max(isNumber(effectiveWidth) ? effectiveWidth : MIN_CHART_WIDTH, MIN_CHART_WIDTH),
Math.max(isNumber(effectiveHeight) ? effectiveHeight : MIN_CHART_HEIGHT, MIN_CHART_HEIGHT),
];
}

View File

@ -0,0 +1,52 @@
import { Graph } from '../../src';
import { createContext, sleep } from './utils';
import './utils/useSnapshotMatchers';
const dir = `${__dirname}/snapshots/auto-resize`;
describe('Auto Resize', () => {
it('autoResize trigger by window.resize', (done) => {
const { backgroundCanvas, canvas, container, labelCanvas, transientCanvas, transientLabelCanvas } = createContext(
500,
500,
);
const graph = new Graph({
width: 500,
height: 500,
autoResize: true,
container,
backgroundCanvas,
canvas,
labelCanvas,
transientCanvas,
transientLabelCanvas,
layout: {
type: 'grid',
},
data: {
nodes: [
{ id: 'node1', data: {} },
{ id: 'node2', data: {} },
{ id: 'node3', data: {} },
{ id: 'node4', data: {} },
],
edges: [{ id: 'edge1', source: 'node1', target: 'node2', data: {} }],
},
});
graph.on('afterlayout', async () => {
container.style.display = 'block';
container.style.width = '400px';
container.style.height = '400px';
window.dispatchEvent(new Event('resize'));
await sleep(500); // auto resize debounce is 300ms.
await expect(canvas).toMatchSVGSnapshot(dir, 'auto-resize');
graph.destroy();
done();
});
});
});

View File

@ -0,0 +1,95 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="400"
height="400"
style="background: transparent; position: absolute; outline: none;"
color-interpolation-filters="sRGB"
tabindex="1"
>
<defs />
<g id="g-svg-camera" transform="matrix(1,0,0,1,0,0)">
<g id="g-root" fill="none" transform="matrix(1,0,0,1,0,0)">
<g id="combo-group" fill="none" transform="matrix(1,0,0,1,0,0)" />
<g id="edge-group" fill="none" transform="matrix(1,0,0,1,0,0)">
<g id="g-svg-24" fill="none" transform="matrix(1,0,0,1,0,0)">
<g transform="matrix(1,0,0,1,141,125)">
<line
id="keyShape"
fill="none"
x1="0"
y1="0"
x2="218"
y2="0"
stroke-width="1"
stroke="rgba(153,173,209,1)"
/>
<line
id="keyShape"
fill="none"
x1="0"
y1="0"
x2="0"
y2="0"
stroke-width="3"
stroke="transparent"
/>
</g>
</g>
</g>
<g id="node-group" fill="none" transform="matrix(1,0,0,1,0,0)">
<g id="g-svg-12" fill="none" transform="matrix(1,0,0,1,125,125)">
<g transform="matrix(1,0,0,1,0,0)">
<circle
id="keyShape"
fill="rgba(34,126,255,1)"
transform="translate(-16,-16)"
cx="16"
cy="16"
r="16"
stroke-width="0"
/>
</g>
</g>
<g id="g-svg-15" fill="none" transform="matrix(1,0,0,1,375,125)">
<g transform="matrix(1,0,0,1,0,0)">
<circle
id="keyShape"
fill="rgba(34,126,255,1)"
transform="translate(-16,-16)"
cx="16"
cy="16"
r="16"
stroke-width="0"
/>
</g>
</g>
<g id="g-svg-18" fill="none" transform="matrix(1,0,0,1,125,375)">
<g transform="matrix(1,0,0,1,0,0)">
<circle
id="keyShape"
fill="rgba(34,126,255,1)"
transform="translate(-16,-16)"
cx="16"
cy="16"
r="16"
stroke-width="0"
/>
</g>
</g>
<g id="g-svg-21" fill="none" transform="matrix(1,0,0,1,375,375)">
<g transform="matrix(1,0,0,1,0,0)">
<circle
id="keyShape"
fill="rgba(34,126,255,1)"
transform="translate(-16,-16)"
cx="16"
cy="16"
r="16"
stroke-width="0"
/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB