perf(canvas): 画布渲染 - 增加节点渲染预算与性能模式控制
This commit is contained in:
@@ -26,7 +26,7 @@ test("world-space approach is independent of viewport scaling", () => {
|
||||
test("only connection targets receive approach data; memo tracks it and selection does not activate tilt", () => {
|
||||
const source = readFileSync(new URL("../src/components/canvas/canvas-node.tsx", import.meta.url), "utf8");
|
||||
const layers = readFileSync(new URL("../src/pages/canvas/canvas-project-world-layers.tsx", import.meta.url), "utf8");
|
||||
expect(source).toContain("isConnectionTarget && !reduceMediaEffects && !dragOffset");
|
||||
expect(source).toContain("isConnectionTarget && !disableMediaEffects && !dragOffset");
|
||||
expect(source).toContain("previous.connectionApproach?.x === next.connectionApproach?.x");
|
||||
expect(source).toContain('data-connection-tilt={connectionTilt ? "true" : undefined}');
|
||||
expect(layers).toContain("props.connectionApproach?.nodeId === node.id ? props.connectionApproach.point : undefined");
|
||||
|
||||
@@ -3,7 +3,7 @@ import { resolve } from "node:path";
|
||||
|
||||
import { describe, expect, test } from "bun:test";
|
||||
|
||||
import { canvasNodeRenderPadding, resolveActiveCanvasMediaNodeId } from "../src/lib/canvas/canvas-performance-mode";
|
||||
import { canvasNodeRenderPadding } from "../src/lib/canvas/canvas-render-budget";
|
||||
import { videoMetadata } from "../src/lib/canvas/canvas-generation-task-sync";
|
||||
import { canvasNodeVideoPreviewReference, canvasNodeVideoPreviewUrl, canvasVideoAssetPreviewUrl } from "../src/lib/canvas/canvas-media-preview";
|
||||
import { collectImageStorageKeys } from "../src/services/image-storage";
|
||||
@@ -34,23 +34,6 @@ function node(id: string, type: CanvasNodeType): CanvasNodeData {
|
||||
return { id, type, title: id, position: { x: 0, y: 0 }, width: 320, height: 180, metadata: {} };
|
||||
}
|
||||
|
||||
describe("canvas media activation", () => {
|
||||
const nodes = [node("video", CanvasNodeType.Video), node("audio", CanvasNodeType.Audio), node("image", CanvasNodeType.Image)];
|
||||
const nodeById = new Map(nodes.map((item) => [item.id, item]));
|
||||
|
||||
test("activates exactly one selected video or audio node", () => {
|
||||
expect(resolveActiveCanvasMediaNodeId(new Set(["video"]), nodeById)).toBe("video");
|
||||
expect(resolveActiveCanvasMediaNodeId(new Set(["audio"]), nodeById)).toBe("audio");
|
||||
});
|
||||
|
||||
test("keeps media inactive for empty, multi-selection, and non-media selection", () => {
|
||||
expect(resolveActiveCanvasMediaNodeId(new Set(), nodeById)).toBeNull();
|
||||
expect(resolveActiveCanvasMediaNodeId(new Set(["video", "audio"]), nodeById)).toBeNull();
|
||||
expect(resolveActiveCanvasMediaNodeId(new Set(["image"]), nodeById)).toBeNull();
|
||||
expect(resolveActiveCanvasMediaNodeId(new Set(["missing"]), nodeById)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("canvas dimension header rendering", () => {
|
||||
test("tracks live viewport scale without React width commits", () => {
|
||||
expect(canvasNodeSource).toContain("calc(var(--canvas-node-width) * var(--canvas-live-scale, 1))");
|
||||
@@ -60,9 +43,8 @@ describe("canvas dimension header rendering", () => {
|
||||
|
||||
describe("large canvas media rendering", () => {
|
||||
test("keeps rendered nodes mounted longer than newly entering nodes", () => {
|
||||
expect(canvasNodeRenderPadding(true, false)).toBe(128);
|
||||
expect(canvasNodeRenderPadding(true, true)).toBe(640);
|
||||
expect(canvasNodeRenderPadding(false, true)).toBeGreaterThan(canvasNodeRenderPadding(false, false));
|
||||
expect(canvasNodeRenderPadding(false)).toBe(192);
|
||||
expect(canvasNodeRenderPadding(true)).toBe(384);
|
||||
});
|
||||
|
||||
test("does not eagerly load or resize LibTV thumbnails", () => {
|
||||
@@ -76,6 +58,12 @@ describe("large canvas media rendering", () => {
|
||||
expect(canvasProjectSource).toContain("<CanvasNodeActionContext.Provider value={canvasNodeActions}>");
|
||||
});
|
||||
|
||||
test("decodes canvas images synchronously so zoom re-rasterization never paints blank frames", () => {
|
||||
const retainedImageSource = canvasNodeContentSource.match(/export function RetainedCanvasImage[\s\S]*?\n}\n/)?.[0] || "";
|
||||
expect(retainedImageSource).toContain('decoding="sync"');
|
||||
expect(retainedImageSource).not.toContain('decoding="async"');
|
||||
});
|
||||
|
||||
test("keeps node shells visible while panning and zooming", () => {
|
||||
expect(globalStylesSource).not.toMatch(/\[data-canvas-viewport-interacting="true"\]\s+\.canvas-node-shell\s*\{[^}]*content-visibility:\s*hidden/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user