fix: fix issue where drag elements under non-default zoom values was abnormal (#5583)

* refactor(behaviors): remove draggableElement of drag-element

* fix(behaviors): fix drag element error when not on default zoom
This commit is contained in:
Aaron 2024-03-22 17:15:38 +08:00 committed by GitHub
parent f451672c7a
commit b57cdfb912
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 70 additions and 37 deletions

View File

@ -31,7 +31,7 @@ export const behaviorDragNode: STDTestCase = async (context) => {
behaviorDragNode.form = (panel) => {
const config = {
enable: true,
hideEdges: 'none',
hideEdge: 'none',
shadow: false,
};
const handleChange = () => {
@ -39,7 +39,7 @@ export const behaviorDragNode: STDTestCase = async (context) => {
};
return [
panel.add(config, 'enable').onChange(handleChange),
panel.add(config, 'hideEdges', ['none', 'in', 'out', 'both']).onChange(handleChange),
panel.add(config, 'hideEdge', ['none', 'in', 'out', 'both']).onChange(handleChange),
panel.add(config, 'shadow').onChange(handleChange),
];
};

View File

@ -1,4 +1,5 @@
import { CommonEvent, type Graph } from '@/src';
import type { Graph } from '@/src';
import { CommonEvent } from '@/src';
import { behaviorDragNode } from '@@/demo/case';
import { createDemoGraph } from '@@/utils';
@ -20,22 +21,22 @@ describe('behavior drag element', () => {
});
it('hide edges', async () => {
graph.setBehaviors([{ type: 'drag-element', hideEdges: 'both' }]);
graph.setBehaviors([{ type: 'drag-element', hideEdge: 'both' }]);
graph.emit(`node:${CommonEvent.DRAG_START}`, { target: { id: 'node-4' }, targetType: 'node' });
graph.emit(`node:${CommonEvent.DRAG}`, { dx: 20, dy: 20 });
await expect(graph).toMatchSnapshot(__filename, 'hideEdges-both');
await expect(graph).toMatchSnapshot(__filename, 'hideEdge-both');
graph.emit(`node:${CommonEvent.DRAG_END}`);
graph.setBehaviors([{ type: 'drag-element', hideEdges: 'in' }]);
graph.setBehaviors([{ type: 'drag-element', hideEdge: 'in' }]);
graph.emit(`node:${CommonEvent.DRAG_START}`, { target: { id: 'node-3' }, targetType: 'node' });
graph.emit(`node:${CommonEvent.DRAG}`, { dx: 0, dy: 20 });
await expect(graph).toMatchSnapshot(__filename, 'hideEdges-in');
await expect(graph).toMatchSnapshot(__filename, 'hideEdge-in');
graph.emit(`node:${CommonEvent.DRAG_END}`);
graph.setBehaviors([{ type: 'drag-element', hideEdges: 'out' }]);
graph.setBehaviors([{ type: 'drag-element', hideEdge: 'out' }]);
graph.emit(`node:${CommonEvent.DRAG_START}`, { target: { id: 'node-3' }, targetType: 'node' });
graph.emit(`node:${CommonEvent.DRAG}`, { dx: 0, dy: 20 });
await expect(graph).toMatchSnapshot(__filename, 'hideEdges-out');
await expect(graph).toMatchSnapshot(__filename, 'hideEdge-out');
graph.emit(`node:${CommonEvent.DRAG_END}`);
});

View File

@ -0,0 +1,40 @@
import { Graph } from '@/src';
import { positionOf } from '@/src/utils/position';
import { createGraphCanvas } from '@@/utils';
describe('behavior drag element bug', () => {
it('drag on non-default zoom', async () => {
const graph = new Graph({
animation: false,
container: createGraphCanvas(document.getElementById('container')),
data: {
nodes: [{ id: 'node-1', style: { x: 100, y: 100 } }],
},
behaviors: ['drag-element'],
});
await graph.draw();
expect(graph.getZoom()).toBe(1);
expect(positionOf(graph.getNodeData('node-1'))).toEqual([100, 100, 0]);
graph.emit('node:dragstart', { target: { id: 'node-1' }, targetType: 'node' });
graph.emit('node:drag', { dx: 20, dy: 20 });
graph.emit('node:dragend');
expect(positionOf(graph.getNodeData('node-1'))).toEqual([120, 120, 0]);
graph.zoomTo(2);
graph.emit('node:dragstart', { target: { id: 'node-1' }, targetType: 'node' });
graph.emit('node:drag', { dx: 20, dy: 20 });
graph.emit('node:dragend');
expect(positionOf(graph.getNodeData('node-1'))).toEqual([130, 130, 0]);
graph.zoomTo(0.5);
graph.emit('node:dragstart', { target: { id: 'node-1' }, targetType: 'node' });
graph.emit('node:drag', { dx: 20, dy: 20 });
graph.emit('node:dragend');
expect(positionOf(graph.getNodeData('node-1'))).toEqual([170, 170, 0]);
graph.destroy();
});
});

View File

@ -8,7 +8,7 @@ import type { BehaviorEvent, EdgeDirection, Point, PrefixObject } from '../types
import { getBBoxSize, getCombinedBBox } from '../utils/bbox';
import { idOf } from '../utils/id';
import { subStyleProps } from '../utils/prefix';
import { subtract } from '../utils/vector';
import { divide, subtract } from '../utils/vector';
import type { BaseBehaviorOptions } from './base-behavior';
import { BaseBehavior } from './base-behavior';
@ -25,12 +25,6 @@ export interface DragElementOptions extends BaseBehaviorOptions, PrefixObject<Ba
* <en/> Whether to enable the function of dragging the node
*/
enable?: boolean | ((event: BehaviorEvent<FederatedMouseEvent> | BehaviorEvent<KeyboardEvent>) => boolean);
/**
* <zh/>
*
* <en/> Supported element types for dragging
*/
draggableElement: ('node' | 'combo')[];
/**
* <zh/>
* - link: 将拖拽元素置入为目标元素的子元素
@ -72,7 +66,7 @@ export interface DragElementOptions extends BaseBehaviorOptions, PrefixObject<Ba
*
* <en/> Edges will not be hidden when using the drag shadow
*/
hideEdges?: 'none' | 'all' | EdgeDirection;
hideEdge?: 'none' | 'all' | EdgeDirection;
/**
* <zh/>
*
@ -84,17 +78,16 @@ export interface DragElementOptions extends BaseBehaviorOptions, PrefixObject<Ba
*
* <en/> Callback when dragging is completed
*/
onfinish?: (ids: ID[]) => void;
onFinish?: (ids: ID[]) => void;
}
export class DragElement extends BaseBehavior<DragElementOptions> {
static defaultOptions: Partial<DragElementOptions> = {
animation: true,
enable: true,
draggableElement: ['node', 'combo'],
enable: (event) => ['node', 'combo'].includes(event.targetType),
dropEffect: 'move',
state: 'selected',
hideEdges: 'none',
hideEdge: 'none',
shadowZIndex: 100,
shadowFill: '#F3F9FF',
shadowFillOpacity: 0.5,
@ -105,6 +98,8 @@ export class DragElement extends BaseBehavior<DragElementOptions> {
private enable: boolean = false;
private enableElements = ['node', 'combo'];
private target: ID[] = [];
private shadow?: Rect;
@ -118,10 +113,6 @@ export class DragElement extends BaseBehavior<DragElementOptions> {
return this.options.animation;
}
private get element() {
return new Set<string>(this.options.draggableElement);
}
constructor(context: RuntimeContext, options: DragElementOptions) {
super(context, Object.assign({}, DragElement.defaultOptions, options));
this.bindEvents();
@ -136,7 +127,7 @@ export class DragElement extends BaseBehavior<DragElementOptions> {
const { graph } = this.context;
this.unbindEvents();
this.element.forEach((type) => {
this.enableElements.forEach((type) => {
graph.on(`${type}:${CommonEvent.DRAG_START}`, this.onDragStart);
graph.on(`${type}:${CommonEvent.DRAG}`, this.onDrag);
graph.on(`${type}:${CommonEvent.DRAG_END}`, this.onDragEnd);
@ -164,17 +155,19 @@ export class DragElement extends BaseBehavior<DragElementOptions> {
if (!this.enable) return;
this.target = this.getSelectedNodeIDs([event.target.id]);
this.hideEdges();
this.hideEdge();
this.context.graph.frontElement(this.target);
if (this.options.shadow) this.createShadow(this.target);
};
private onDrag = (event: DragEvent) => {
if (!this.enable) return;
const zoom = this.context.graph.getZoom();
const { dx, dy } = event;
const delta = divide([dx, dy], zoom);
if (this.options.shadow) this.moveShadow([dx, dy]);
else this.moveElement(this.target, [dx, dy]);
if (this.options.shadow) this.moveShadow(delta);
else this.moveElement(this.target, delta);
};
private onDragEnd = () => {
@ -187,7 +180,7 @@ export class DragElement extends BaseBehavior<DragElementOptions> {
this.moveElement(this.target, [dx, dy]);
}
this.showEdges();
this.options.onfinish?.(this.target);
this.options.onFinish?.(this.target);
this.target = [];
};
@ -209,7 +202,6 @@ export class DragElement extends BaseBehavior<DragElementOptions> {
private validate(event: DragEvent) {
if (this.destroyed) return false;
if (!this.element.has(event.targetType)) return false;
const { enable } = this.options;
if (isFunction(enable)) return enable(event);
return !!enable;
@ -270,14 +262,14 @@ export class DragElement extends BaseBehavior<DragElementOptions> {
this.hiddenEdges = [];
}
private hideEdges() {
const { hideEdges, shadow } = this.options;
if (hideEdges === 'none' || shadow) return;
private hideEdge() {
const { hideEdge, shadow } = this.options;
if (hideEdge === 'none' || shadow) return;
const { graph } = this.context;
if (hideEdges === 'all') this.hiddenEdges = graph.getEdgeData().map(idOf);
if (hideEdge === 'all') this.hiddenEdges = graph.getEdgeData().map(idOf);
else {
this.hiddenEdges = Array.from(
new Set(this.target.map((id) => graph.getRelatedEdgesData(id, hideEdges).map(idOf)).flat()),
new Set(this.target.map((id) => graph.getRelatedEdgesData(id, hideEdge).map(idOf)).flat()),
);
}
graph.hideElement(this.hiddenEdges);
@ -286,7 +278,7 @@ export class DragElement extends BaseBehavior<DragElementOptions> {
private unbindEvents() {
const { graph } = this.context;
this.element.forEach((type) => {
this.enableElements.forEach((type) => {
graph.off(`${type}:${CommonEvent.DRAG_START}`, this.onDragStart);
graph.off(`${type}:${CommonEvent.DRAG}`, this.onDrag);
graph.off(`${type}:${CommonEvent.DRAG_END}`, this.onDragEnd);