diff --git a/packages/g6/__tests__/demos/plugin-snapline.ts b/packages/g6/__tests__/demos/plugin-snapline.ts
index e31af8feb6b..05b5df1827b 100644
--- a/packages/g6/__tests__/demos/plugin-snapline.ts
+++ b/packages/g6/__tests__/demos/plugin-snapline.ts
@@ -19,7 +19,7 @@ export const pluginSnapline: TestCase = async (context) => {
labelText: (datum) => datum.id,
},
},
- behaviors: ['drag-element', 'drag-canvas'],
+ behaviors: ['drag-element', 'drag-canvas', 'zoom-canvas'],
plugins: [
{
type: 'snapline',
@@ -36,6 +36,7 @@ export const pluginSnapline: TestCase = async (context) => {
const config = {
filter: false,
offset: 20,
+ autoSnap: false,
};
pluginSnapline.form = (panel) => {
@@ -48,7 +49,6 @@ export const pluginSnapline: TestCase = async (context) => {
key: 'snapline',
filter: (node: Node) => (filter ? node.id !== 'node3' : true),
});
- graph.render();
}),
panel
.add(config, 'offset', [0, 20, Infinity])
@@ -58,7 +58,15 @@ export const pluginSnapline: TestCase = async (context) => {
key: 'snapline',
offset,
});
- graph.render();
+ }),
+ panel
+ .add(config, 'autoSnap')
+ .name('Auto Snap')
+ .onChange((autoSnap: boolean) => {
+ graph.updatePlugin({
+ key: 'snapline',
+ autoSnap,
+ });
}),
];
};
diff --git a/packages/g6/__tests__/snapshots/behaviors/brush-select/brush-selecting-zoom.svg b/packages/g6/__tests__/snapshots/behaviors/brush-select/brush-selecting-zoom.svg
new file mode 100644
index 00000000000..fea0e1ad314
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/behaviors/brush-select/brush-selecting-zoom.svg
@@ -0,0 +1,93 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/snapshots/plugins/snapline/zoom-5.svg b/packages/g6/__tests__/snapshots/plugins/snapline/zoom-5.svg
new file mode 100644
index 00000000000..5dbd4d68edd
--- /dev/null
+++ b/packages/g6/__tests__/snapshots/plugins/snapline/zoom-5.svg
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/packages/g6/__tests__/unit/behaviors/brush-select.spec.ts b/packages/g6/__tests__/unit/behaviors/brush-select.spec.ts
index 1c07a530a21..67722af67b7 100644
--- a/packages/g6/__tests__/unit/behaviors/brush-select.spec.ts
+++ b/packages/g6/__tests__/unit/behaviors/brush-select.spec.ts
@@ -128,6 +128,14 @@ describe('behavior brush select', () => {
graph.emit(CanvasEvent.CLICK);
await expect(graph).toMatchSnapshot(__filename, 'brush-clear-mode-intersect');
+
+ // zoom to test line width
+ graph.zoomTo(5);
+ graph.emit(CommonEvent.POINTER_DOWN, { canvas: { x: 100, y: 100 }, targetType: 'canvas' });
+ graph.emit(CommonEvent.POINTER_MOVE, { canvas: { x: 250, y: 400 } });
+
+ await expect(graph).toMatchSnapshot(__filename, 'brush-selecting-zoom');
+ graph.emit(CommonEvent.POINTER_UP, { canvas: { x: 250, y: 400 } });
});
afterAll(() => {
diff --git a/packages/g6/__tests__/unit/plugins/snapline.spec.ts b/packages/g6/__tests__/unit/plugins/snapline.spec.ts
index 4f988aa22d3..a39f542330b 100644
--- a/packages/g6/__tests__/unit/plugins/snapline.spec.ts
+++ b/packages/g6/__tests__/unit/plugins/snapline.spec.ts
@@ -1,4 +1,5 @@
-import { Node, NodeEvent, type Graph } from '@/src';
+import type { Graph, Node } from '@/src';
+import { NodeEvent } from '@/src';
import { pluginSnapline } from '@@/demos';
import { createDemoGraph } from '../../utils';
@@ -75,6 +76,16 @@ describe('plugin snapline', () => {
graph.emit(NodeEvent.DRAG, { target: node, dx: 0, dy: 0 });
await expect(graph).toMatchSnapshot(__filename, `auto-snap`);
graph.emit(NodeEvent.DRAG_END, { target: node });
+
+ // zoom to test lineWidth
+ graph.zoomTo(5, false, [300, 300]);
+ graph.updatePlugin({ key: 'snapline', autoSnap: false });
+ graph.updateNodeData([{ id: 'node3', style: { x: 260, y: 300 } }]);
+ graph.render();
+ graph.emit(NodeEvent.DRAG_START, { target: node, targetType: 'node' });
+ graph.emit(NodeEvent.DRAG, { target: node, dx: 0, dy: 0 });
+ await expect(graph).toMatchSnapshot(__filename, 'zoom-5');
+ graph.emit(NodeEvent.DRAG_END, { target: node });
});
afterAll(() => {
diff --git a/packages/g6/__tests__/unit/utils/shortcut.spec.ts b/packages/g6/__tests__/unit/utils/shortcut.spec.ts
index d39555cf7ad..db36d3484c8 100644
--- a/packages/g6/__tests__/unit/utils/shortcut.spec.ts
+++ b/packages/g6/__tests__/unit/utils/shortcut.spec.ts
@@ -80,6 +80,24 @@ describe('shortcut', () => {
expect(drag).toHaveBeenCalledTimes(1);
expect(drag.mock.calls[0][0].deltaX).toBe(10);
expect(drag.mock.calls[0][0].deltaY).toBe(0);
+
+ drag.mockClear();
+
+ // shift drag
+ emitter.emit(CommonEvent.KEY_DOWN, { key: 'Shift' });
+ emitter.emit(CommonEvent.DRAG, { deltaX: 10, deltaY: 0 });
+ emitter.emit(CommonEvent.KEY_UP, { key: 'Shift' });
+ expect(drag).toHaveBeenCalledTimes(0);
+
+ shortcut.unbindAll();
+ shortcut.bind(['Shift', 'drag'], drag);
+
+ emitter.emit(CommonEvent.KEY_DOWN, { key: 'Shift' });
+ emitter.emit(CommonEvent.DRAG, { deltaX: 10, deltaY: 0 });
+ emitter.emit(CommonEvent.KEY_UP, { key: 'Shift' });
+ expect(drag).toHaveBeenCalledTimes(1);
+ expect(drag.mock.calls[0][0].deltaX).toBe(10);
+ expect(drag.mock.calls[0][0].deltaY).toBe(0);
});
it('focus', () => {
diff --git a/packages/g6/src/behaviors/brush-select.ts b/packages/g6/src/behaviors/brush-select.ts
index 998a3ff5940..95dee0fee82 100644
--- a/packages/g6/src/behaviors/brush-select.ts
+++ b/packages/g6/src/behaviors/brush-select.ts
@@ -144,9 +144,16 @@ export class BrushSelect extends BaseBehavior {
*/
protected onPointerDown(event: IPointerEvent) {
if (!this.validate(event) || !this.isKeydown() || this.startPoint) return;
- const { canvas } = this.context;
+ const { canvas, graph } = this.context;
+ const style = { ...this.options.style };
- this.rectShape = new Rect({ id: 'g6-brush-select', style: this.options.style });
+ // 根据缩放比例调整 lineWidth
+ // Adjust lineWidth according to the zoom ratio
+ if (this.options.style.lineWidth) {
+ style.lineWidth = +this.options.style.lineWidth / graph.getZoom();
+ }
+
+ this.rectShape = new Rect({ id: 'g6-brush-select', style });
canvas.appendChild(this.rectShape);
this.startPoint = [event.canvas.x, event.canvas.y];
@@ -327,8 +334,7 @@ export class BrushSelect extends BaseBehavior {
protected isKeydown(): boolean {
const { trigger } = this.options;
const keys = (Array.isArray(trigger) ? trigger : [trigger]) as string[];
- if (!trigger || keys.includes('drag')) return true;
- return this.shortcut!.match(keys);
+ return this.shortcut!.match(keys.filter((key) => key !== 'drag'));
}
/**
@@ -348,7 +354,6 @@ export class BrushSelect extends BaseBehavior {
private bindEvents() {
const { graph } = this.context;
- this.unbindEvents();
graph.on(CommonEvent.POINTER_DOWN, this.onPointerDown);
graph.on(CommonEvent.POINTER_MOVE, this.onPointerMove);
@@ -373,7 +378,9 @@ export class BrushSelect extends BaseBehavior {
* @internal
*/
public update(options: Partial) {
+ this.unbindEvents();
this.options = deepMix(this.options, options);
+ this.bindEvents();
}
/**
diff --git a/packages/g6/src/plugins/snapline/index.ts b/packages/g6/src/plugins/snapline/index.ts
index 7ff3ef40a7c..f6469881bd3 100644
--- a/packages/g6/src/plugins/snapline/index.ts
+++ b/packages/g6/src/plugins/snapline/index.ts
@@ -4,6 +4,7 @@ import { NodeEvent } from '../../constants';
import type { RuntimeContext } from '../../runtime/types';
import type { ID, IDragEvent, Node } from '../../types';
import { isVisible } from '../../utils/element';
+import { divide } from '../../utils/vector';
import type { BasePluginOptions } from '../base-plugin';
import { BasePlugin } from '../base-plugin';
@@ -133,27 +134,38 @@ export class Snapline extends BasePlugin {
this.verticalLine.style.visibility = 'hidden';
}
+ private getLineWidth(direction: 'horizontal' | 'vertical') {
+ const { lineWidth } = this.options[`${direction}LineStyle`] as LineStyleProps;
+ return +(lineWidth || defaultLineStyle.lineWidth || 1) / this.context.graph.getZoom();
+ }
+
private updateSnapline(metadata: Metadata) {
const { verticalX, verticalMinY, verticalMaxY, horizontalY, horizontalMinX, horizontalMaxX } = metadata;
const [canvasWidth, canvasHeight] = this.context.canvas.getSize();
const { offset } = this.options;
if (horizontalY !== null) {
- this.horizontalLine.style.x1 = offset === Infinity ? 0 : horizontalMinX! - offset;
- this.horizontalLine.style.y1 = horizontalY;
- this.horizontalLine.style.x2 = offset === Infinity ? canvasWidth : horizontalMaxX! + offset;
- this.horizontalLine.style.y2 = horizontalY;
- this.horizontalLine.style.visibility = 'visible';
+ Object.assign(this.horizontalLine.style, {
+ x1: offset === Infinity ? 0 : horizontalMinX! - offset,
+ y1: horizontalY,
+ x2: offset === Infinity ? canvasWidth : horizontalMaxX! + offset,
+ y2: horizontalY,
+ visibility: 'visible',
+ lineWidth: this.getLineWidth('horizontal'),
+ });
} else {
this.horizontalLine.style.visibility = 'hidden';
}
if (verticalX !== null) {
- this.verticalLine.style.x1 = verticalX;
- this.verticalLine.style.y1 = offset === Infinity ? 0 : verticalMinY! - offset;
- this.verticalLine.style.x2 = verticalX;
- this.verticalLine.style.y2 = offset === Infinity ? canvasHeight : verticalMaxY! + offset;
- this.verticalLine.style.visibility = 'visible';
+ Object.assign(this.verticalLine.style, {
+ x1: verticalX,
+ y1: offset === Infinity ? 0 : verticalMinY! - offset,
+ x2: verticalX,
+ y2: offset === Infinity ? canvasHeight : verticalMaxY! + offset,
+ visibility: 'visible',
+ lineWidth: this.getLineWidth('vertical'),
+ });
} else {
this.verticalLine.style.visibility = 'hidden';
}
@@ -194,25 +206,37 @@ export class Snapline extends BasePlugin {
}
};
+ /**
+ * Get the delta of the drag
+ * @param event - drag event object
+ * @returns delta
+ * @internal
+ */
+ protected getDelta(event: IDragEvent) {
+ const zoom = this.context.graph.getZoom();
+ return divide([event.dx, event.dy], zoom);
+ }
+
private enableSnap = (event: IDragEvent) => {
const { target } = event;
const threshold = 0.5;
if (this.isHorizontalSticking || this.isVerticalSticking) {
+ const [dx, dy] = this.getDelta(event);
if (
this.isHorizontalSticking &&
this.isVerticalSticking &&
- Math.abs(event.dx) <= threshold &&
- Math.abs(event.dy) <= threshold
+ Math.abs(dx) <= threshold &&
+ Math.abs(dy) <= threshold
) {
- this.context.graph.translateElementBy({ [target.id]: [-event.dx, -event.dy] }, false);
+ this.context.graph.translateElementBy({ [target.id]: [-dx, -dy] }, false);
return false;
- } else if (this.isHorizontalSticking && Math.abs(event.dy) <= threshold) {
- this.context.graph.translateElementBy({ [target.id]: [0, -event.dy] }, false);
+ } else if (this.isHorizontalSticking && Math.abs(dy) <= threshold) {
+ this.context.graph.translateElementBy({ [target.id]: [0, -dy] }, false);
return false;
- } else if (this.isVerticalSticking && Math.abs(event.dx) <= threshold) {
- this.context.graph.translateElementBy({ [target.id]: [-event.dx, 0] }, false);
+ } else if (this.isVerticalSticking && Math.abs(dx) <= threshold) {
+ this.context.graph.translateElementBy({ [target.id]: [-dx, 0] }, false);
return false;
} else {
this.isHorizontalSticking = false;
diff --git a/packages/g6/src/runtime/viewport.ts b/packages/g6/src/runtime/viewport.ts
index 5a354944c08..cfb053aca62 100644
--- a/packages/g6/src/runtime/viewport.ts
+++ b/packages/g6/src/runtime/viewport.ts
@@ -34,12 +34,17 @@ export class ViewportController {
const { canvas } = this.context;
return new Proxy(canvas.getCamera(), {
get: (target, prop: keyof ICamera) => {
- const transientCamera = canvas.getLayer('transient').getCamera();
+ const layers = Object.entries(canvas.getLayers()).filter(([name]) => !['main'].includes(name));
+ const cameras = layers.map(([, layer]) => layer.getCamera());
+
const value = target[prop];
if (typeof value === 'function') {
return (...args: any[]) => {
const result = (value as (...args: any[]) => any).apply(target, args);
- (transientCamera[prop] as (...args: any[]) => any).apply(transientCamera, args);
+ cameras.forEach((camera) => {
+ (camera[prop] as (...args: any[]) => any).apply(camera, args);
+ });
+
return result;
};
}