Files
ttstd-infinite-canvas/web/src/lib/canvas/canvas-performance-mode.ts
ddCat 3f4a9ea3d0 feat(canvas): 画布工作台 - 新增生成副本并修复大型画布性能、素材同步与删除校验
统一图片与视频副本的生成落点并保留参考连接。\n优化大型画布节点虚拟化、媒体懒加载、拖拽与详情进入路径。\n修复素材主版本 ID 同步及删除时的画布独立引用校验。
2026-09-01 14:57:05 +08:00

53 lines
2.1 KiB
TypeScript

import { CanvasNodeType, type CanvasMediaPerformanceMode, type CanvasNodeData } from "@/types/canvas";
const STORAGE_KEY = "canvas-media-performance-mode";
// The DOM budget is deliberately below the connection budget. Image-heavy
// canvases pay a texture/compositing cost per mounted card, even when the
// cards themselves are already virtualized spatially.
export const CANVAS_MAX_RENDERED_NODES = 720;
export const CANVAS_MAX_RENDERED_CONNECTIONS = 5000;
export function readCanvasMediaPerformanceMode(): CanvasMediaPerformanceMode {
try {
const stored = window.localStorage.getItem(STORAGE_KEY);
return stored === "quality" || stored === "performance" ? stored : "auto";
} catch {
return "auto";
}
}
export function persistCanvasMediaPerformanceMode(mode: CanvasMediaPerformanceMode) {
try {
window.localStorage.setItem(STORAGE_KEY, mode);
} catch {
// 浏览器禁用本地存储时保留当前会话内的选择。
}
}
export function shouldReduceCanvasMediaEffects(mode: CanvasMediaPerformanceMode, nodes: CanvasNodeData[]) {
if (mode === "performance") return true;
if (mode === "quality") return false;
const mediaCount = nodes.filter((node) => node.type === CanvasNodeType.Image || node.type === CanvasNodeType.Video || node.type === CanvasNodeType.Audio).length;
return nodes.length >= 80 || mediaCount >= 32;
}
export function canvasNodeRenderPadding(reduceMediaEffects: boolean, previouslyRendered: boolean) {
if (previouslyRendered) return reduceMediaEffects ? 640 : 384;
return reduceMediaEffects ? 128 : 192;
}
export function canvasNodeRenderBudget(scale: number) {
if (scale < 0.14) return 280;
if (scale < 0.28) return 420;
return CANVAS_MAX_RENDERED_NODES;
}
export function resolveActiveCanvasMediaNodeId(selectedNodeIds: ReadonlySet<string>, nodeById: ReadonlyMap<string, CanvasNodeData>) {
if (selectedNodeIds.size !== 1) return null;
const nodeId = selectedNodeIds.values().next().value;
if (!nodeId) return null;
const type = nodeById.get(nodeId)?.type;
return type === CanvasNodeType.Video || type === CanvasNodeType.Audio ? nodeId : null;
}