Throttle canvas rendering for large rooms; cap displayed size and use DPR scaling

This commit is contained in:
2026-10-17 22:07:01 -04:00
parent f596a48899
commit 12e01b9ac9
3 changed files with 67 additions and 16 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>furniture-app</title> <title>furniture-app</title>
<script type="module" crossorigin src="/assets/index-z6fd8Sn7.js"></script> <script type="module" crossorigin src="/assets/index-BIL7D6zM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-9k24Ci9h.css"> <link rel="stylesheet" crossorigin href="/assets/index-9k24Ci9h.css">
</head> </head>
<body> <body>
+14 -2
View File
@@ -308,11 +308,23 @@ export function ControlPanel({
<div className="grid-2"> <div className="grid-2">
<label className="field"> <label className="field">
<span>Width</span> <span>Width</span>
<NumberField value={state.room.width} min={100} width="100%" onChange={(v) => setRoom({ width: v })} /> <NumberField
value={state.room.width}
min={100}
max={5000}
width="100%"
onChange={(v) => setRoom({ width: v })}
/>
</label> </label>
<label className="field"> <label className="field">
<span>Height</span> <span>Height</span>
<NumberField value={state.room.height} min={100} width="100%" onChange={(v) => setRoom({ height: v })} /> <NumberField
value={state.room.height}
min={100}
max={5000}
width="100%"
onChange={(v) => setRoom({ height: v })}
/>
</label> </label>
</div> </div>
</div> </div>
+52 -13
View File
@@ -33,6 +33,38 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect, s
return () => clearInterval(t); return () => clearInterval(t);
}, [showPaths]); }, [showPaths]);
const [viewport, setViewport] = useState({ width: 1200, height: 800 });
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const resize = () => {
if (containerRef.current) {
setViewport({
width: Math.floor(containerRef.current.clientWidth),
height: Math.floor(containerRef.current.clientHeight),
});
}
};
resize();
window.addEventListener('resize', resize);
return () => window.removeEventListener('resize', resize);
}, []);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const dpr = Math.max(1, window.devicePixelRatio || 1);
const baseW = Math.min(state.room.width, viewport.width || state.room.width);
const baseH = Math.min(state.room.height, viewport.height || state.room.height);
canvas.style.width = `${baseW}px`;
canvas.style.height = `${baseH}px`;
canvas.width = baseW * dpr;
canvas.height = baseH * dpr;
const ctx = canvas.getContext('2d');
if (ctx) ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
draw();
}, [state.room.width, state.room.height, zoom, viewport]);
useEffect(() => { useEffect(() => {
draw(); draw();
}, [state, selectedId, mode, zoom, showPaths, phase]); }, [state, selectedId, mode, zoom, showPaths, phase]);
@@ -42,9 +74,14 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect, s
if (!canvas) return; if (!canvas) return;
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
if (!ctx) return; if (!ctx) return;
ctx.clearRect(0, 0, canvas.width, canvas.height); const dpr = Math.max(1, window.devicePixelRatio || 1);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);
ctx.save(); ctx.save();
ctx.scale(zoom, zoom); const scaleX = (canvas.width / dpr) / state.room.width;
const scaleY = (canvas.height / dpr) / state.room.height;
const s = Math.min(scaleX, scaleY);
ctx.scale(s, s);
ctx.fillStyle = '#12151b'; ctx.fillStyle = '#12151b';
ctx.fillRect(0, 0, state.room.width, state.room.height); ctx.fillRect(0, 0, state.room.width, state.room.height);
@@ -308,17 +345,19 @@ export function RoomCanvas({ state, onStateChange, mode, selectedId, onSelect, s
}; };
return ( return (
<canvas <div ref={containerRef} style={{ width: '100%', height: '100%' }}>
ref={canvasRef} <canvas
width={Math.max(state.room.width, 400)} ref={canvasRef}
height={Math.max(state.room.height, 300)} width={Math.min(state.room.width, 2000)}
style={{ maxWidth: '100%', height: 'auto' }} height={Math.min(state.room.height, 2000)}
onMouseDown={handleMouseDown} style={{ maxWidth: '100%', maxHeight: '100%', width: '100%', height: '100%', objectFit: 'contain' }}
onMouseMove={handleMouseMove} onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp} onMouseMove={handleMouseMove}
onDragOver={handleDragOver} onMouseUp={handleMouseUp}
onDrop={handleDrop} onDragOver={handleDragOver}
/> onDrop={handleDrop}
/>
</div>
); );
} }