79 lines
2.2 KiB
TypeScript
79 lines
2.2 KiB
TypeScript
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;
|
|
}
|