功能增加

This commit is contained in:
andy committed 2026-09-11 15:33:18 +08:00
1 parent b340fc7ad7
commit f1993eb388
69 files changed
+2243 -162

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
export type CanvasSize = { width: number; height: number };
export type CanvasView = { zoom: number; x: number; y: number };
export type CanvasPosition = { x: number; y: number };
export const MIN_CANVAS_ZOOM = 0.25;
export const MAX_CANVAS_ZOOM = 8;
export const FIT_CANVAS_VIEW: CanvasView = { zoom: 1, x: 0, y: 0 };
export function fitCanvasImage(image: CanvasSize, viewport: CanvasSize): CanvasSize {
if (image.width <= 0 || image.height <= 0 || viewport.width <= 0 || viewport.height <= 0) return { width: 0, height: 0 };
const scale = Math.min(Math.max(1, viewport.width - 20) / image.width, Math.max(1, viewport.height - 20) / image.height);
return { width: image.width * scale, height: image.height * scale };
}
export function constrainCanvasView(view: CanvasView, fittedImage: CanvasSize, viewport: CanvasSize): CanvasView {
const zoom = Math.max(MIN_CANVAS_ZOOM, Math.min(MAX_CANVAS_ZOOM, Number.isFinite(view.zoom) ? view.zoom : 1));
const maxX = Math.max(0, (fittedImage.width * zoom - viewport.width) / 2);
const maxY = Math.max(0, (fittedImage.height * zoom - viewport.height) / 2);
return {
zoom,
x: Math.max(-maxX, Math.min(maxX, Number.isFinite(view.x) ? view.x : 0)),
y: Math.max(-maxY, Math.min(maxY, Number.isFinite(view.y) ? view.y : 0))
};
}
// The anchor is relative to the viewport center. Preserve the image point
// under the cursor when zooming, except where an image edge limits panning.
export function zoomCanvasAt(view: CanvasView, requestedZoom: number, anchor: CanvasPosition, fittedImage: CanvasSize, viewport: CanvasSize): CanvasView {
const zoom = Math.max(MIN_CANVAS_ZOOM, Math.min(MAX_CANVAS_ZOOM, requestedZoom));
const factor = zoom / view.zoom;
return constrainCanvasView({ zoom, x: anchor.x - (anchor.x - view.x) * factor, y: anchor.y - (anchor.y - view.y) * factor }, fittedImage, viewport);
}
// Use the *transformed image bounds*, never the clipped viewport bounds.
// Persisted annotations and provider prompts remain in the 0–1000 space.
export function canvasAnnotationPoint(point: CanvasPosition, bounds: CanvasSize & { left: number; top: number }): CanvasPosition | null {
if (bounds.width <= 0 || bounds.height <= 0) return null;
const clamp = (value: number) => Math.max(0, Math.min(1000, Math.round(value)));
return { x: clamp((point.x - bounds.left) / bounds.width * 1000), y: clamp((point.y - bounds.top) / bounds.height * 1000) };
}