feat(canvas): 画布引擎 - 引入 LeaferJS 图形层并优化缩放清晰度与性能
This commit is contained in:
24
web/src/lib/canvas/canvas-leafer-viewport.ts
Normal file
24
web/src/lib/canvas/canvas-leafer-viewport.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { ViewportTransform } from "@/types/canvas";
|
||||
|
||||
const MIN_VIEWPORT_SCALE = 0.05;
|
||||
const MIN_PREVIEW_RATIO = 0.85;
|
||||
const MAX_PREVIEW_RATIO = 1.25;
|
||||
|
||||
export function calculateCanvasPreviewTransform(viewport: ViewportTransform, rasterViewport: ViewportTransform) {
|
||||
const ratio = viewport.k / Math.max(rasterViewport.k, MIN_VIEWPORT_SCALE);
|
||||
// 将旧 backing store 的屏幕坐标映射到实时视口,避免缩放手势逐帧触发矢量重绘。
|
||||
return {
|
||||
ratio,
|
||||
x: viewport.x - rasterViewport.x * ratio,
|
||||
y: viewport.y - rasterViewport.y * ratio,
|
||||
};
|
||||
}
|
||||
|
||||
export function shouldRebaseCanvasRaster(viewport: ViewportTransform, rasterViewport: ViewportTransform) {
|
||||
const { ratio } = calculateCanvasPreviewTransform(viewport, rasterViewport);
|
||||
return ratio < MIN_PREVIEW_RATIO || ratio > MAX_PREVIEW_RATIO;
|
||||
}
|
||||
|
||||
export function sameCanvasViewport(left: ViewportTransform, right: ViewportTransform) {
|
||||
return left.x === right.x && left.y === right.y && left.k === right.k;
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
import type { ViewportTransform } from "@/types/canvas";
|
||||
import type { SelectionBox, ViewportTransform } from "@/types/canvas";
|
||||
|
||||
export const CANVAS_VIEWPORT_PREVIEW_EVENT = "canvas:viewport-preview";
|
||||
export const CANVAS_SELECTION_PREVIEW_EVENT = "canvas:selection-preview";
|
||||
|
||||
export function applyCanvasLiveViewport(container: HTMLDivElement | null, viewport: ViewportTransform, notify = true) {
|
||||
if (!container) return;
|
||||
const gridSize = 48 * viewport.k;
|
||||
const committedScale = Number(container.style.getPropertyValue("--canvas-committed-scale")) || viewport.k;
|
||||
container.style.setProperty("--canvas-live-x", `${viewport.x}px`);
|
||||
container.style.setProperty("--canvas-live-y", `${viewport.y}px`);
|
||||
container.style.setProperty("--canvas-live-scale", String(viewport.k));
|
||||
container.style.setProperty("--canvas-live-scale-ratio", String(viewport.k / committedScale));
|
||||
container.style.setProperty("--canvas-grid-size", `${gridSize}px`);
|
||||
container.style.setProperty("--canvas-grid-x", `${viewport.x % gridSize}px`);
|
||||
container.style.setProperty("--canvas-grid-y", `${viewport.y % gridSize}px`);
|
||||
@@ -24,3 +27,13 @@ export function subscribeCanvasViewportPreview(container: HTMLDivElement, listen
|
||||
container.addEventListener(CANVAS_VIEWPORT_PREVIEW_EVENT, handlePreview);
|
||||
return () => container.removeEventListener(CANVAS_VIEWPORT_PREVIEW_EVENT, handlePreview);
|
||||
}
|
||||
|
||||
export function applyCanvasSelectionPreview(container: HTMLDivElement | null, selection: SelectionBox) {
|
||||
container?.dispatchEvent(new CustomEvent<SelectionBox>(CANVAS_SELECTION_PREVIEW_EVENT, { detail: selection }));
|
||||
}
|
||||
|
||||
export function subscribeCanvasSelectionPreview(container: HTMLDivElement, listener: (selection: SelectionBox) => void) {
|
||||
const handlePreview = (event: Event) => listener((event as CustomEvent<SelectionBox>).detail);
|
||||
container.addEventListener(CANVAS_SELECTION_PREVIEW_EVENT, handlePreview);
|
||||
return () => container.removeEventListener(CANVAS_SELECTION_PREVIEW_EVENT, handlePreview);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user