UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

122 lines (121 loc) 5.21 kB
import { EventedComponent } from "../../components/canvas/EventedComponent/EventedComponent"; import { getXY, isMetaKeyEvent, isTrackpadWheelEvent, isWindows } from "../../utils/functions"; import { clamp } from "../../utils/functions/clamp"; import { dragListener } from "../../utils/functions/dragListener"; import { EVENTS } from "../../utils/types/events"; export class Camera extends EventedComponent { constructor(props, parent) { super(props, parent); this.handleClick = () => { this.context.graph.api.unsetSelection(); }; this.handleMouseDownEvent = (event) => { if (!this.context.graph.rootStore.settings.getConfigFlag("canDragCamera") || !(event instanceof MouseEvent)) { return; } if (!isMetaKeyEvent(event)) { dragListener(this.ownerDocument) .on(EVENTS.DRAG_START, (event) => this.onDragStart(event)) .on(EVENTS.DRAG_UPDATE, (event) => this.onDragUpdate(event)) .on(EVENTS.DRAG_END, () => this.onDragEnd()); } }; this.handleWheelEvent = (event) => { if (!this.context.graph.rootStore.settings.getConfigFlag("canZoomCamera")) { return; } event.stopPropagation(); event.preventDefault(); const isMoveEvent = isTrackpadWheelEvent(event) && !isMetaKeyEvent(event); if (isMoveEvent) { const windows = isWindows(); this.moveWithEdges(windows && event.shiftKey ? -event.deltaY : -event.deltaX, windows && event.shiftKey ? -event.deltaX : -event.deltaY); return; } const xy = getXY(this.context.canvas, event); if (!event.deltaY) return; /** * Speed of wheel/trackpad pinch * * The zoom event from the trackpad pass the deltaY as a floating number, which can be less than +1/-1. * If the delta is less than 1, it causes the zoom speed to slow down. * Therefore, we have to round the value of deltaY to 1 if it is less than or equal to 1. */ const pinchSpeed = Math.sign(event.deltaY) * clamp(Math.abs(event.deltaY), 1, 20); const dScale = this.context.constants.camera.STEP * this.context.constants.camera.SPEED * pinchSpeed; const cameraScale = this.camera.getCameraScale(); // Smooth scale. The closer you get, the higher the speed const smoothDScale = dScale * cameraScale; this.camera.zoom(xy[0], xy[1], cameraScale - smoothDScale); }; this.camera = this.context.camera; this.ownerDocument = this.context.ownerDocument; this.addWheelListener(); this.addEventListener("click", this.handleClick); this.addEventListener("mousedown", this.handleMouseDownEvent); } setRoot() { this.setContext({ root: this.props.root, }); this.addWheelListener(this.props.root); } addWheelListener(root = this.props.root) { root?.addEventListener("wheel", this.handleWheelEvent, { passive: false }); } propsChanged(nextProps) { if (this.props.root !== nextProps.root) { this.props.root?.removeEventListener("wheel", this.handleWheelEvent); this.addWheelListener(nextProps.root); } super.propsChanged(nextProps); } unmount() { super.unmount(); this.props.root?.removeEventListener("wheel", this.handleWheelEvent); this.removeEventListener("mousedown", this.handleMouseDownEvent); } onDragStart(event) { this.lastDragEvent = event; } onDragUpdate(event) { if (!this.lastDragEvent) { return; } this.camera.move(event.pageX - this.lastDragEvent.pageX, event.pageY - this.lastDragEvent.pageY); this.lastDragEvent = event; } onDragEnd() { this.lastDragEvent = undefined; } moveWithEdges(deltaX, deltaY) { const uR = this.context.graph.api.getUsableRect(); const cameraState = this.camera.getCameraState(); const gapX = cameraState.relativeWidth; const gapY = cameraState.relativeHeight; const moveToRight = deltaX > 0; const moveToLeft = deltaX < 0; const moveToTop = deltaY > 0; const moveToBottop = deltaY < 0; if (moveToRight && uR.x - gapX > cameraState.relativeX * -1) { deltaX = 0; } // left if (moveToLeft && uR.x + uR.width + gapX < cameraState.relativeX * -1 + cameraState.relativeWidth) { deltaX = 0; } // right if (moveToTop && uR.y - gapY > cameraState.relativeY * -1) { deltaY = 0; } // top if (moveToBottop && uR.y + uR.height + gapY < cameraState.relativeY * -1 + cameraState.relativeHeight) { deltaY = 0; } // bottom this.camera.move(deltaX, deltaY); } render() { this.context.layer.resetTransform(); } updateChildren() { return this.props.children; } }