@antv/g6
Version:
A Graph Visualization Framework in JavaScript
218 lines (199 loc) • 6.39 kB
text/typescript
import { clamp, isFunction } from '@antv/util';
import { CommonEvent } from '../constants';
import type { RuntimeContext } from '../runtime/types';
import type {
IKeyboardEvent,
IPointerEvent,
IWheelEvent,
Point,
PointObject,
ViewportAnimationEffectTiming,
} from '../types';
import { parsePoint } from '../utils/point';
import type { ShortcutKey } from '../utils/shortcut';
import { Shortcut } from '../utils/shortcut';
import type { BaseBehaviorOptions } from './base-behavior';
import { BaseBehavior } from './base-behavior';
/**
* <zh/> 缩放画布交互配置项
*
* <en/> Zoom canvas behavior options
*/
export interface ZoomCanvasOptions extends BaseBehaviorOptions {
/**
* <zh/> 是否启用缩放动画
*
* <en/> Whether to enable the animation of zooming
* @defaultValue '{ duration: 200 }'
*/
animation?: ViewportAnimationEffectTiming;
/**
* <zh/> 是否启用缩放画布的功能
*
* <en/> Whether to enable the function of zooming the canvas
* @defaultValue true
*/
enable?: boolean | ((event: IWheelEvent | IKeyboardEvent | IPointerEvent) => boolean);
/**
* <zh/> 缩放中心点(视口坐标)
* - 默认情况下为鼠标位置中心
*
* <en/> zoom center(viewport coordinates)
* - by default , the center is the mouse position center
*/
origin?: Point;
/**
* <zh/> 触发缩放的方式
* - ShortcutKey:组合快捷键,**默认使用滚轮缩放**,['Control'] 表示按住 Control 键滚动鼠标滚轮时触发缩放
* - CombinationKey:缩放快捷键,例如 { zoomIn: ['Control', '+'], zoomOut: ['Control', '-'], reset: ['Control', '0'] }
*
* <en/> The way to trigger zoom
* - ShortcutKey: Combination shortcut key, **default to zoom with the mouse wheel**, ['Control'] means zooming when holding down the Control key and scrolling the mouse wheel
* - CombinationKey: Zoom shortcut key, such as { zoomIn: ['Control', '+'], zoomOut: ['Control', '-'], reset: ['Control', '0'] }
*/
trigger?:
| ShortcutKey
| {
zoomIn: ShortcutKey;
zoomOut: ShortcutKey;
reset: ShortcutKey;
};
/**
* <zh/> 缩放灵敏度
*
* <en/> Zoom sensitivity
* @defaultValue 1
*/
sensitivity?: number;
/**
* <zh/> 完成缩放时的回调
*
* <en/> Callback when zooming is completed
*/
onFinish?: () => void;
/**
* <zh/> 是否阻止默认事件
*
* <en/> Whether to prevent the default event
* @defaultValue true
*/
preventDefault?: boolean;
}
/**
* <zh/> 缩放画布交互
*
* <en/> Zoom canvas behavior
*/
export class ZoomCanvas extends BaseBehavior<ZoomCanvasOptions> {
static defaultOptions: Partial<ZoomCanvasOptions> = {
animation: { duration: 200 },
enable: true,
sensitivity: 1,
trigger: [],
preventDefault: true,
};
private shortcut: Shortcut;
constructor(context: RuntimeContext, options: ZoomCanvasOptions) {
super(context, Object.assign({}, ZoomCanvas.defaultOptions, options));
this.shortcut = new Shortcut(context.graph);
this.bindEvents();
}
/**
* <zh/> 更新配置
*
* <en/> Update options
* @param options - <zh/> 配置项 | <en/> Options
* @internal
*/
public update(options: Partial<ZoomCanvasOptions>): void {
super.update(options);
this.bindEvents();
}
private bindEvents() {
const { trigger } = this.options;
this.shortcut.unbindAll();
if (Array.isArray(trigger)) {
if (trigger.includes(CommonEvent.PINCH)) {
this.shortcut.bind([CommonEvent.PINCH], (event) => {
this.zoom(event.scale, event, false);
});
} else {
const container = this.context.canvas.getContainer();
container?.addEventListener(CommonEvent.WHEEL, this.preventDefault);
this.shortcut.bind([...trigger, CommonEvent.WHEEL], (event) => {
const { deltaX, deltaY } = event;
this.zoom(-(deltaY ?? deltaX), event, false);
});
}
}
if (typeof trigger === 'object') {
const {
zoomIn = [],
zoomOut = [],
reset = [],
} = trigger as {
zoomIn: ShortcutKey;
zoomOut: ShortcutKey;
reset: ShortcutKey;
};
this.shortcut.bind(zoomIn, (event) => this.zoom(10, event, this.options.animation));
this.shortcut.bind(zoomOut, (event) => this.zoom(-10, event, this.options.animation));
this.shortcut.bind(reset, this.onReset);
}
}
/**
* <zh/> 缩放画布
*
* <en/> Zoom canvas
* @param value - <zh/> 缩放值, > 0 放大, < 0 缩小 | <en/> Zoom value, > 0 zoom in, < 0 zoom out
* @param event - <zh/> 事件对象 | <en/> Event object
* @param animation - <zh/> 缩放动画配置 | <en/> Zoom animation configuration
*/
protected zoom = async (
value: number,
event: IWheelEvent | IKeyboardEvent | IPointerEvent,
animation: ZoomCanvasOptions['animation'],
) => {
if (!this.validate(event)) return;
const { graph } = this.context;
let origin: Point | undefined = this.options.origin;
if (!origin && 'viewport' in event) {
origin = parsePoint(event.viewport as PointObject);
}
const { sensitivity, onFinish } = this.options;
const ratio = 1 + (clamp(value, -50, 50) * sensitivity) / 100;
const zoom = graph.getZoom();
await graph.zoomTo(zoom * ratio, animation, origin);
onFinish?.();
};
protected onReset = async () => {
await this.context.graph.zoomTo(1, this.options.animation);
};
/**
* <zh/> 验证是否可以缩放
*
* <en/> Verify whether it can be zoomed
* @param event - <zh/> 事件对象 | <en/> Event object
* @returns <zh/> 是否可以缩放 | <en/> Whether it can be zoomed
* @internal
*/
protected validate(event: IWheelEvent | IKeyboardEvent | IPointerEvent) {
if (this.destroyed) return false;
const { enable } = this.options;
if (isFunction(enable)) return enable(event);
return !!enable;
}
private preventDefault = (event: Event) => {
if (this.options.preventDefault) event.preventDefault();
};
/**
* <zh/> 销毁缩放画布
*
* <en/> Destroy zoom canvas
*/
public destroy() {
this.shortcut.destroy();
this.context.canvas.getContainer()?.removeEventListener(CommonEvent.WHEEL, this.preventDefault);
super.destroy();
}
}