import type { Dispatch } from "@commands/dispatcher"; import type { GlobalPointerConsumer, GlobalWheelConsumer, PointerInputEvent, WheelInputEvent } from "./pointer"; export type ViewportPointerPanHandler = { pointerDown(event: PointerInputEvent): boolean; pointerMove(event: PointerInputEvent): boolean; pointerUp(event: PointerInputEvent): boolean; }; export function createViewportPointerPanHandler(options: { globalConsumer: GlobalPointerConsumer; dispatch: Dispatch; getCurrentZoom: () => number; canPan?: (event: PointerInputEvent) => boolean; }): ViewportPointerPanHandler { let activePointerId: number | undefined; let lastPosition: PointerInputEvent["position"] | undefined; const canPan = options.canPan ?? defaultCanPan; return { pointerDown(event) { if (options.globalConsumer(event)) return true; if (!canPan(event)) return false; activePointerId = event.pointerId; lastPosition = event.position; return true; }, pointerMove(event) { if (activePointerId !== event.pointerId || !lastPosition) return false; const zoom = options.getCurrentZoom(); const screenDelta = { x: event.position.x - lastPosition.x, y: event.position.y - lastPosition.y, }; lastPosition = event.position; options.dispatch("viewport.pan", { delta: { x: -screenDelta.x / zoom, y: -screenDelta.y / zoom, }, }); return true; }, pointerUp(event) { if (activePointerId !== event.pointerId) return false; activePointerId = undefined; lastPosition = undefined; return true; }, }; } function defaultCanPan(event: PointerInputEvent) { return event.pointerType !== "mouse" || (event.buttons & 4) === 4; } export function handleViewportWheel(options: { event: WheelInputEvent; globalConsumer: GlobalWheelConsumer; dispatch: Dispatch; currentZoom: number; }): boolean { if (options.globalConsumer(options.event)) return true; const zoomFactor = Math.exp(-options.event.delta.y * 0.001); options.dispatch("viewport.zoomAroundPoint", { zoom: options.currentZoom * zoomFactor, point: options.event.position, }); return true; }