Skip to content

Commit

Permalink
feat(behavior): scroll-canvas/drag-canvas support range (#6266)
Browse files Browse the repository at this point in the history
* feat: scroll-canvas supports range

* feat: drag-canvas supports range

* test: add range tests

* fix: fix cr issue
  • Loading branch information
yvonneyx authored Sep 3, 2024
1 parent b7bf987 commit 9c04450
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 8 deletions.
20 changes: 20 additions & 0 deletions packages/g6/__tests__/unit/behaviors/drag-canvas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,24 @@ describe('behavior drag canvas', () => {

await expect(graph).toMatchSnapshot(__filename, 'drag-on-element');
});

it('range', () => {
graph.updateBehavior({ key: 'drag-canvas', trigger: 'drag', direction: 'both', range: 0.5 });

const emitDragEvent = (dx: number, dy: number, count: number) => {
for (let i = 0; i < count; i++) {
dispatchCanvasEvent(graph, CommonEvent.DRAG_START, { targetType: 'canvas' });
dispatchCanvasEvent(graph, CommonEvent.DRAG, { movement: { x: dx, y: dy }, targetType: 'canvas' });
dispatchCanvasEvent(graph, CommonEvent.DRAG_END);
}
};

const [canvasWidth, canvasHeight] = graph.getCanvas().getSize();
emitDragEvent(10, 0, 60);
expect(graph.getPosition()[0]).toBeCloseTo(canvasWidth / 2);
emitDragEvent(-10, 0, 60);
expect(graph.getPosition()[0]).toBeCloseTo(-canvasWidth / 2);
emitDragEvent(0, -10, 60);
expect(graph.getPosition()[0]).toBeCloseTo(-canvasHeight / 2);
});
});
26 changes: 25 additions & 1 deletion packages/g6/__tests__/unit/behaviors/scroll-canvas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,15 @@ describe('behavior scroll canvas', () => {

it('direction', async () => {
setBehavior({ direction: 'x' });
const [x, y] = graph.getPosition();
let [x, y] = graph.getPosition();
emitWheelEvent({ deltaX: -10, deltaY: -10 });
expect(graph.getPosition()).toBeCloseTo([x + 10, y]);

setBehavior({ direction: 'y' });
[x, y] = graph.getPosition();
emitWheelEvent({ deltaX: -10, deltaY: -10 });
expect(graph.getPosition()).toBeCloseTo([x, y + 10]);

setBehavior({ direction: undefined });
});

Expand Down Expand Up @@ -98,6 +103,25 @@ describe('behavior scroll canvas', () => {
expect(graph.getPosition()).toBeCloseTo([x + 10, y]);
});

it('range', () => {
graph.setBehaviors((behavior) => [...behavior, { ...shortcutScrollCanvasOptions, range: 0.5 }]);

const emitArrow = (key: 'ArrowRight' | 'ArrowLeft' | 'ArrowUp' | 'ArrowDown', count: number) => {
for (let i = 0; i < count; i++) {
graph.emit(CommonEvent.KEY_DOWN, { key });
graph.emit(CommonEvent.KEY_UP, { key });
}
};

const [canvasWidth, canvasHeight] = graph.getCanvas().getSize();
emitArrow('ArrowRight', 50);
expect(graph.getPosition()[0]).toBeCloseTo(canvasWidth / 2);
emitArrow('ArrowLeft', 50);
expect(graph.getPosition()[0]).toBeCloseTo(-canvasWidth / 2);
emitArrow('ArrowUp', 50);
expect(graph.getPosition()[1]).toBeCloseTo(-canvasHeight / 2);
});

it('destroy', () => {
graph.destroy();
});
Expand Down
45 changes: 42 additions & 3 deletions packages/g6/src/behaviors/drag-canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { debounce, isObject } from '@antv/util';
import { CommonEvent } from '../constants';
import type { RuntimeContext } from '../runtime/types';
import type { IKeyboardEvent, IPointerEvent, Vector2, ViewportAnimationEffectTiming } from '../types';
import { getExpandedBBox, getPointBBox, isPointInBBox } from '../utils/bbox';
import { parsePadding } from '../utils/padding';
import type { ShortcutKey } from '../utils/shortcut';
import { Shortcut } from '../utils/shortcut';
import { multiply } from '../utils/vector';
import { multiply, subtract } from '../utils/vector';
import type { BaseBehaviorOptions } from './base-behavior';
import { BaseBehavior } from './base-behavior';

Expand Down Expand Up @@ -41,6 +43,13 @@ export interface DragCanvasOptions extends BaseBehaviorOptions {
* @defaultValue `'both'`
*/
direction?: 'x' | 'y' | 'both';
/**
* <zh/> 可拖拽的视口范围,默认最多可拖拽一屏。可以分别设置上、右、下、左四个方向的范围,每个方向的范围在 [0, Infinity] 之间
*
* <en/> The draggable viewport range allows you to drag up to one screen by default. You can set the range for each direction (top, right, bottom, left) individually, with each direction's range between [0, Infinity]
* @defaultValue 1
*/
range?: number | number[];
/**
* <zh/> 触发拖拽的方式,默认使用指针按下拖拽
*
Expand Down Expand Up @@ -80,6 +89,7 @@ export class DragCanvas extends BaseBehavior<DragCanvasOptions> {
},
sensitivity: 10,
direction: 'both',
range: 1,
};

private shortcut: Shortcut;
Expand Down Expand Up @@ -169,16 +179,45 @@ export class DragCanvas extends BaseBehavior<DragCanvasOptions> {
* @internal
*/
protected async translate(offset: Vector2, animation?: ViewportAnimationEffectTiming) {
let [dx, dy] = offset;
offset = this.clampByDirection(offset);
offset = this.clampByRange(offset);

await this.context.graph.translateBy(offset, animation);
}

private clampByDirection([dx, dy]: Vector2): Vector2 {
const { direction } = this.options;
if (direction === 'x') {
dy = 0;
} else if (direction === 'y') {
dx = 0;
}
return [dx, dy];
}

await this.context.graph.translateBy([dx, dy], animation);
private clampByRange([dx, dy]: Vector2): Vector2 {
const { viewport, canvas } = this.context;

const [canvasWidth, canvasHeight] = canvas.getSize();
const [top, right, bottom, left] = parsePadding(this.options.range);
const range = [canvasHeight * top, canvasWidth * right, canvasHeight * bottom, canvasWidth * left];
const draggableArea = getExpandedBBox(getPointBBox(viewport!.getCanvasCenter()), range);

const nextViewportCenter = subtract(viewport!.getViewportCenter(), [dx, dy, 0]);
if (!isPointInBBox(nextViewportCenter, draggableArea)) {
const {
min: [minX, minY],
max: [maxX, maxY],
} = draggableArea;

if ((nextViewportCenter[0] < minX && dx > 0) || (nextViewportCenter[0] > maxX && dx < 0)) {
dx = 0;
}
if ((nextViewportCenter[1] < minY && dy > 0) || (nextViewportCenter[1] > maxY && dy < 0)) {
dy = 0;
}
}
return [dx, dy];
}

private validate(event: IPointerEvent | IKeyboardEvent) {
Expand Down
49 changes: 45 additions & 4 deletions packages/g6/src/behaviors/scroll-canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { isFunction, isObject } from '@antv/util';
import { CommonEvent } from '../constants';
import type { RuntimeContext } from '../runtime/types';
import type { IKeyboardEvent, Point } from '../types';
import { getExpandedBBox, getPointBBox, isPointInBBox } from '../utils/bbox';
import { parsePadding } from '../utils/padding';
import { Shortcut, ShortcutKey } from '../utils/shortcut';
import { multiply, subtract } from '../utils/vector';
import type { BaseBehaviorOptions } from './base-behavior';
import { BaseBehavior } from './base-behavior';

Expand Down Expand Up @@ -42,6 +45,13 @@ export interface ScrollCanvasOptions extends BaseBehaviorOptions {
* - `'y'`: only allow vertical scrolling
*/
direction?: 'x' | 'y';
/**
* <zh/> 可滚动的视口范围,默认最多可滚动一屏。可以分别设置上、右、下、左四个方向的范围,每个方向的范围在 [0, Infinity] 之间
*
* <en/> The scrollable viewport range allows you to scroll up to one screen by default. You can set the range for each direction (top, right, bottom, left) individually, with each direction's range between [0, Infinity]
* @defaultValue 1
*/
range?: number | number[];
/**
* <zh/> 滚动灵敏度
*
Expand Down Expand Up @@ -74,6 +84,7 @@ export class ScrollCanvas extends BaseBehavior<ScrollCanvasOptions> {
enable: true,
sensitivity: 1,
preventDefault: true,
range: 0.5,
};

private shortcut: Shortcut;
Expand Down Expand Up @@ -130,18 +141,48 @@ export class ScrollCanvas extends BaseBehavior<ScrollCanvasOptions> {
await this.scroll([-diffX, -diffY], event);
};

private formatDisplacement([dx, dy]: Point) {
const { direction, sensitivity } = this.options;
private formatDisplacement(d: Point) {
const { sensitivity } = this.options;

dx = dx * sensitivity;
dy = dy * sensitivity;
d = multiply(d, sensitivity);
d = this.clampByDirection(d);
d = this.clampByRange(d);

return d;
}

private clampByDirection([dx, dy]: Point) {
const { direction } = this.options;
if (direction === 'x') {
dy = 0;
} else if (direction === 'y') {
dx = 0;
}
return [dx, dy] as Point;
}

private clampByRange([dx, dy]: Point) {
const { viewport, canvas } = this.context;

const [canvasWidth, canvasHeight] = canvas.getSize();
const [top, right, bottom, left] = parsePadding(this.options.range);
const range = [canvasHeight * top, canvasWidth * right, canvasHeight * bottom, canvasWidth * left];
const scrollableArea = getExpandedBBox(getPointBBox(viewport!.getCanvasCenter()), range);

const nextViewportCenter = subtract(viewport!.getViewportCenter(), [dx, dy, 0]);
if (!isPointInBBox(nextViewportCenter, scrollableArea)) {
const {
min: [minX, minY],
max: [maxX, maxY],
} = scrollableArea;

if ((nextViewportCenter[0] < minX && dx > 0) || (nextViewportCenter[0] > maxX && dx < 0)) {
dx = 0;
}
if ((nextViewportCenter[1] < minY && dy > 0) || (nextViewportCenter[1] > maxY && dy < 0)) {
dy = 0;
}
}
return [dx, dy] as Point;
}

Expand Down

0 comments on commit 9c04450

Please sign in to comment.