import { CameraType } from '@antv/g';
import type { BaseBehaviorOptions, IDragEvent, RuntimeContext, ShortcutKey } from '@antv/g6';
import { BaseBehavior, GraphEvent, Shortcut } from '@antv/g6';
/**
* 观察 3D 画布交互配置项
*
* Observe 3D canvas options
*/
export interface ObserveCanvas3DOptions extends BaseBehaviorOptions {
enable?: boolean;
/**
* 相机模式
* - `orbiting` 固定视点(`focalPoint`),改变相机位置。不能跨越南北极
* - `exploring` 固定视点(`focalPoint`),改变相机位置。可以跨越南北极
* - `tracking` 第一人称模式,固定相机位置,改变视点(`focalPoint`)位置
*
* Camera mode
* - `orbiting` Fixed viewpoint(`focalPoint`), change camera position. Cannot cross the north and south poles
* - `exploring` Fixed viewpoint(`focalPoint`), change camera position. Can cross the north and south poles
* - `tracking` First-person mode, fixed camera position, change viewpoint(`focalPoint`) position
*/
mode?: 'orbiting' | 'exploring' | 'tracking';
/**
* 按下该快捷键配合指针观察场景
*
* Press this shortcut key to observe the scene with the pointer
*/
trigger?: ShortcutKey;
/**
* 灵敏度
*
* Sensitivity
*/
sensitivity?: number;
}
/**
* 3D 场景控制器,提供缩放、平移、旋转等能力
*
* 3D scene controller, providing zoom, pan, rotate and other capabilities
*/
export class ObserveCanvas3D extends BaseBehavior {
static defaultOptions: Partial = {
enable: true,
mode: 'orbiting',
trigger: [],
};
private shortcut: Shortcut;
private get camera() {
return this.context.canvas.getCamera();
}
constructor(context: RuntimeContext, options: ObserveCanvas3DOptions) {
super(context, { ...ObserveCanvas3D.defaultOptions, ...options });
this.shortcut = new Shortcut(context.graph);
this.bindEvents();
}
public update(options: Partial): void {
super.update(options);
this.setCameraType();
}
private setCameraType = () => {
const { mode } = this.options;
const CameraModeMap = {
orbiting: CameraType.ORBITING,
exploring: CameraType.EXPLORING,
tracking: CameraType.TRACKING,
};
this.camera.setType(CameraModeMap[mode]);
};
// tracking 模式下需要减速,否则容易出现抖动
// Deceleration is required in tracking mode, otherwise jitter is easy to occur
private getRatio() {
const { sensitivity, mode } = this.options;
if (sensitivity) return sensitivity / 10;
if (mode === 'tracking') return 0.1;
return 1;
}
private onDrag = (event: IDragEvent) => {
if (!this.options.enable) return;
const { x, y } = event.movement;
const ratio = this.getRatio();
this.camera.rotate(x * ratio, -y * ratio, 0);
};
private bindEvents() {
const { graph } = this.context;
graph.once(GraphEvent.BEFORE_DRAW, this.setCameraType);
this.shortcut.unbindAll();
this.shortcut.bind([...this.options.trigger, 'drag'], this.onDrag);
}
public destroy() {
this.shortcut.destroy();
super.destroy();
}
}