Skip to content

Commit

Permalink
feat(behavior): hide elements when zooming, scrolling and dragging ca…
Browse files Browse the repository at this point in the history
…nvas (#6054)

* feat(behavior): hide elements when zooming,scrolling and dragging canvas

* fix: fix cr issues

* test: supplement viewport transfron test

* refactor: rename ViewportEvent to IViewportEvent
  • Loading branch information
yvonneyx authored Jul 17, 2024
1 parent fd2d790 commit 3f775f9
Show file tree
Hide file tree
Showing 13 changed files with 4,562 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Graph } from '@/src';
import data from '@@/dataset/cluster.json';

export const behaviorOptimizeViewportTransform: TestCase = async (context) => {
const graph = new Graph({
...context,
data,
layout: {
type: 'd3-force',
},
node: {
style: {
iconSrc: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
labelFontSize: 8,
labelText: (datum) => datum.id,
size: 20,
},
},
edge: {
style: {
labelFontSize: 8,
labelText: (datum) => datum.id,
},
},
behaviors: ['drag-canvas', 'zoom-canvas', 'scroll-canvas', 'optimize-viewport-transform'],
});

await graph.render();

return graph;
};
1 change: 1 addition & 0 deletions packages/g6/__tests__/demos/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export { behaviorExpandCollapseNode } from './behavior-expand-collapse-node';
export { behaviorFocusElement } from './behavior-focus-element';
export { behaviorHoverActivate } from './behavior-hover-activate';
export { behaviorLassoSelect } from './behavior-lasso-select';
export { behaviorOptimizeViewportTransform } from './behavior-optimize-viewport-transform';
export { behaviorScrollCanvas } from './behavior-scroll-canvas';
export { behaviorZoomCanvas } from './behavior-zoom-canvas';
export { caseIndentedTree } from './case-indented-tree';
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { behaviorOptimizeViewportTransform } from '@/__tests__/demos';
import { GraphEvent, type Graph } from '@/src';
import { createDemoGraph } from '@@/utils';

describe('behavior optimize canvas', () => {
let graph: Graph;

beforeAll(async () => {
graph = await createDemoGraph(behaviorOptimizeViewportTransform, { animation: false });
});

it('viewport', async () => {
await expect(graph).toMatchSnapshot(__filename);

graph.emit(GraphEvent.BEFORE_TRANSFORM, {
type: GraphEvent.BEFORE_TRANSFORM,
data: {
mode: 'relative',
translate: [0, -3],
},
});
await expect(graph).toMatchSnapshot(__filename, 'viewport-change');
graph.emit(GraphEvent.AFTER_TRANSFORM, {
type: GraphEvent.AFTER_TRANSFORM,
data: {
mode: 'relative',
translate: [0, -1],
},
});
await expect(graph).toMatchSnapshot(__filename, 'after-viewport-change');
});

it('destroy', () => {
graph.destroy();
});
});
2 changes: 2 additions & 0 deletions packages/g6/src/behaviors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export { DragElementForce } from './drag-element-force';
export { FocusElement } from './focus-element';
export { HoverActivate } from './hover-activate';
export { LassoSelect } from './lasso-select';
export { OptimizeViewportTransform } from './optimize-viewport-transform';
export { ScrollCanvas } from './scroll-canvas';
export { ZoomCanvas } from './zoom-canvas';

Expand All @@ -23,5 +24,6 @@ export type { DragElementForceOptions } from './drag-element-force';
export type { FocusElementOptions } from './focus-element';
export type { HoverActivateOptions } from './hover-activate';
export type { LassoSelectOptions } from './lasso-select';
export type { OptimizeViewportTransformOptions } from './optimize-viewport-transform';
export type { ScrollCanvasOptions } from './scroll-canvas';
export type { ZoomCanvasOptions } from './zoom-canvas';
127 changes: 127 additions & 0 deletions packages/g6/src/behaviors/optimize-viewport-transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import type { BaseStyleProps, DisplayObject } from '@antv/g';
import { debounce, isFunction } from '@antv/util';
import { GraphEvent } from '../constants';
import type { RuntimeContext } from '../runtime/types';
import type { IViewportEvent } from '../types/event';
import { setVisibility } from '../utils/visibility';
import type { BaseBehaviorOptions } from './base-behavior';
import { BaseBehavior } from './base-behavior';

/**
* <zh/> 画布优化交互配置项
*
* <en/> Canvas optimization behavior options
*/
export interface OptimizeViewportTransformOptions extends BaseBehaviorOptions {
/**
* <zh/> 是否启用画布优化功能
*
* <en/> Whether to enable canvas optimization function
* @defaultValue true
*/
enable?: boolean | ((event: IViewportEvent) => boolean);
/**
* <zh/> 始终保留的图形类名。操作画布过程中会隐藏元素reservedShapes(除了指定类名的图形),以提高性能
*
* <en/> Persistently reserved shape classnames. Elements are hidden during canvas manipulation (except for shapes with specified classnames) to enhance performance.
* @defaultValue `{ node: ['key'] }`
*/
shapes?: {
node?: string[];
edge?: string[];
combo?: string[];
};
/**
* <zh/> 设置防抖时间
*
* <en/> Set debounce time
* @defaultValue 200
*/
debounce?: number;
}

/**
* <zh/> 操作画布过程中隐藏元素
*
* <en/> Hide elements during canvas operations (dragging, zooming, scrolling)
*/
export class OptimizeViewportTransform extends BaseBehavior<OptimizeViewportTransformOptions> {
static defaultOptions: Partial<OptimizeViewportTransformOptions> = {
enable: true,
debounce: 200,
shapes: { node: ['key'] },
};

private isVisible: boolean = true;

constructor(context: RuntimeContext, options: OptimizeViewportTransformOptions) {
super(context, Object.assign({}, OptimizeViewportTransform.defaultOptions, options));
this.bindEvents();
}

private filterShapes = (shapes: DisplayObject[], classnames?: string[]) => {
return shapes.filter((shape) => shape.className && !classnames?.includes(shape.className));
};

private setElementsVisibility = (
elements: DisplayObject[],
visibility: BaseStyleProps['visibility'],
excludedClassnames?: string[],
) => {
elements.forEach((element) => {
setVisibility(
element,
visibility,
excludedClassnames && ((shapes) => this.filterShapes(shapes, excludedClassnames)),
);
});
};

private hideShapes = (event: IViewportEvent) => {
if (!this.validate(event) || !this.isVisible) return;

const { element } = this.context;
const { shapes = {} } = this.options;
this.setElementsVisibility(element!.getNodes(), 'hidden', shapes.node);
this.setElementsVisibility(element!.getEdges(), 'hidden', shapes.edge);
this.setElementsVisibility(element!.getCombos(), 'hidden', shapes.combo);
this.isVisible = false;
};

private showShapes = debounce((event: IViewportEvent) => {
if (!this.validate(event) || this.isVisible) return;

const { element } = this.context;
this.setElementsVisibility(element!.getNodes(), 'visible');
this.setElementsVisibility(element!.getEdges(), 'visible');
this.setElementsVisibility(element!.getCombos(), 'visible');
this.isVisible = true;
}, this.options.debounce);

private bindEvents() {
const { graph } = this.context;

graph.on(GraphEvent.BEFORE_TRANSFORM, this.hideShapes);
graph.on(GraphEvent.AFTER_TRANSFORM, this.showShapes);
}

private unbindEvents() {
const { graph } = this.context;

graph.off(GraphEvent.BEFORE_TRANSFORM, this.hideShapes);
graph.off(GraphEvent.AFTER_TRANSFORM, this.showShapes);
}

private validate(event: IViewportEvent) {
if (this.destroyed) return false;

const { enable } = this.options;
if (isFunction(enable)) return enable(event);
return !!enable;
}

public destroy() {
this.unbindEvents();
super.destroy();
}
}
18 changes: 10 additions & 8 deletions packages/g6/src/registry/build-in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
FocusElement,
HoverActivate,
LassoSelect,
OptimizeViewportTransform,
ScrollCanvas,
ZoomCanvas,
} from '../behaviors';
Expand Down Expand Up @@ -94,18 +95,19 @@ export const BUILT_IN_EXTENSIONS: ExtensionRegistry = {
translate: Translate,
},
behavior: {
'zoom-canvas': ZoomCanvas,
'brush-select': BrushSelect,
'click-select': ClickSelect,
'collapse-expand': CollapseExpand,
'create-edge': CreateEdge,
'drag-canvas': DragCanvas,
'drag-element': DragElement,
'drag-element-force': DragElementForce,
'scroll-canvas': ScrollCanvas,
'collapse-expand': CollapseExpand,
'click-select': ClickSelect,
'hover-activate': HoverActivate,
'drag-element': DragElement,
'focus-element': FocusElement,
'create-edge': CreateEdge,
'brush-select': BrushSelect,
'hover-activate': HoverActivate,
'lasso-select': LassoSelect,
'optimize-viewport-transform': OptimizeViewportTransform,
'scroll-canvas': ScrollCanvas,
'zoom-canvas': ZoomCanvas,
},
combo: {
circle: CircleCombo,
Expand Down
11 changes: 9 additions & 2 deletions packages/g6/src/utils/visibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@ const PropertyKey = 'visibility';
* <en/> Set the visibility of the shape instance
* @param shape - <zh/> 图形实例 | <en/> shape instance
* @param visibility - <zh/> 可见性 | <en/> visibility
* @param filter - <zh/> 筛选出需要设置可见性的图形 | <en/> Filter out the shapes that need to set visibility
* @remarks
* <zh/> 在设置 enableCSSParsing 为 false 的情况下,复合图形无法继承父属性,因此需要对所有子图形应用相同的可见性
*
* <en/> After setting enableCSSParsing to false, the compound shape cannot inherit the parent attribute, so the same visibility needs to be applied to all child shapes
*/
export function setVisibility(shape: DisplayObject, visibility: BaseStyleProps['visibility']) {
const shapes = [shape, ...getDescendantShapes(shape)];
export function setVisibility(
shape: DisplayObject,
visibility: BaseStyleProps['visibility'],
filter?: (shapes: DisplayObject[]) => DisplayObject[],
) {
let shapes = [shape, ...getDescendantShapes(shape)];

if (filter) shapes = filter?.(shapes);

shapes.forEach((sp) => {
if (!hasCachedStyle(sp, PropertyKey)) cacheStyle(sp, PropertyKey);
Expand Down
16 changes: 16 additions & 0 deletions packages/site/examples/behavior/canvas/demo/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@
"en": "Scroll Y"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*c5DCSoAYsZQAAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "zoom.js",
"title": {
"zh": "缩放画布",
"en": "Zoom Canvas"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*1AXoRY9Bw-0AAAAAAAAAAAAADmJ7AQ/original"
},
{
"filename": "optimize.js",
"title": {
"zh": "拖拽缩放画布时隐藏元素",
"en": "Hide Elements When Dragging and Zooming"
},
"screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*AZ4IRJkIZr8AAAAAAAAAAAAADmJ7AQ/original"
}
]
}
27 changes: 27 additions & 0 deletions packages/site/examples/behavior/canvas/demo/optimize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Graph } from '@antv/g6';

const graph = new Graph({
container: 'container',
layout: {
type: 'grid',
},
data: {
nodes: [{ id: 'node1' }, { id: 'node2' }, { id: 'node3' }, { id: 'node4' }, { id: 'node5' }],
edges: [
{ source: 'node1', target: 'node2' },
{ source: 'node1', target: 'node3' },
{ source: 'node1', target: 'node4' },
{ source: 'node2', target: 'node3' },
{ source: 'node3', target: 'node4' },
{ source: 'node4', target: 'node5' },
],
},
node: {
style: {
labelText: (datum) => datum.id,
},
},
behaviors: ['zoom-canvas', 'drag-canvas', 'scroll-canvas', 'optimize-viewport-transform'],
});

graph.render();
22 changes: 22 additions & 0 deletions packages/site/examples/behavior/canvas/demo/zoom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Graph } from '@antv/g6';

const graph = new Graph({
container: 'container',
layout: {
type: 'grid',
},
data: {
nodes: [{ id: 'node1' }, { id: 'node2' }, { id: 'node3' }, { id: 'node4' }, { id: 'node5' }],
edges: [
{ source: 'node1', target: 'node2' },
{ source: 'node1', target: 'node3' },
{ source: 'node1', target: 'node4' },
{ source: 'node2', target: 'node3' },
{ source: 'node3', target: 'node4' },
{ source: 'node4', target: 'node5' },
],
},
behaviors: ['zoom-canvas'],
});

graph.render();

0 comments on commit 3f775f9

Please sign in to comment.