UNPKG

@antv/g6

Version:

A Graph Visualization Framework in JavaScript

218 lines (199 loc) 6.39 kB
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(); } }