SVG preview fitting size problem solved

This commit is contained in:
2026-06-09 20:58:37 +08:00
parent fa0ebe899c
commit 7195dea7cd
2 changed files with 76 additions and 26 deletions
+63 -13
View File
@@ -2246,24 +2246,73 @@ Organization : OptiHK Limited
// Displays generated layout SVG previews with zoom and pan controls.
const LayoutSvgPreview = ({ page }) => {
const [layoutScale, setLayoutScale] = useState(100);
const [layoutScale, setLayoutScale] = useState(null);
const [previewViewport, setPreviewViewport] = useState({ width: 1, height: 1 });
const [svgSize, setSvgSize] = useState(null);
const previewCanvasRef = useRef(null);
const previewBounds = useMemo(
() => page.layoutBounds || calculateLayoutBounds(page),
[page.layoutBounds, page.nodes, page.canvasSize]
);
const normalizedScale = Math.min(800, Math.max(10, Number(layoutScale) || 100));
const stageWidth = Math.max(1, previewBounds.width) * normalizedScale / 100;
const stageHeight = Math.max(1, previewBounds.height) * normalizedScale / 100;
const minLayoutScale = 0.01;
const maxLayoutScale = 800;
const scalePrecision = 100;
const clampLayoutScale = (value, fallback = 100) => {
const numericValue = Number(value);
const scale = Number.isFinite(numericValue) && numericValue > 0 ? numericValue : fallback;
return Number(Math.min(maxLayoutScale, Math.max(minLayoutScale, scale)).toFixed(2));
};
const baseWidth = Math.max(1, svgSize?.width || previewBounds.width);
const baseHeight = Math.max(1, svgSize?.height || previewBounds.height);
const availableWidth = Math.max(1, previewViewport.width);
const availableHeight = Math.max(1, previewViewport.height);
const rawFitScalePercent = Math.min(availableWidth / baseWidth, availableHeight / baseHeight) * 100;
const fitScalePercent = clampLayoutScale(Math.floor(rawFitScalePercent * scalePrecision) / scalePrecision);
const normalizedScale = clampLayoutScale(layoutScale ?? fitScalePercent, fitScalePercent);
const stageWidth = baseWidth * normalizedScale / 100;
const stageHeight = baseHeight * normalizedScale / 100;
useEffect(() => {
const previewCanvas = previewCanvasRef.current;
if (!previewCanvas) return undefined;
const measurePreviewViewport = () => {
const styles = window.getComputedStyle(previewCanvas);
const paddingX = (parseFloat(styles.paddingLeft) || 0) + (parseFloat(styles.paddingRight) || 0);
const paddingY = (parseFloat(styles.paddingTop) || 0) + (parseFloat(styles.paddingBottom) || 0);
setPreviewViewport({
width: Math.max(1, previewCanvas.clientWidth - paddingX),
height: Math.max(1, previewCanvas.clientHeight - paddingY)
});
};
measurePreviewViewport();
if (typeof ResizeObserver === 'undefined') {
window.addEventListener('resize', measurePreviewViewport);
return () => window.removeEventListener('resize', measurePreviewViewport);
}
const observer = new ResizeObserver(measurePreviewViewport);
observer.observe(previewCanvas);
return () => observer.disconnect();
}, []);
const updateScale = (value) => {
setLayoutScale(Math.min(800, Math.max(10, Number(value) || 100)));
setLayoutScale(clampLayoutScale(value, fitScalePercent));
};
const handleWheel = (event) => {
event.preventDefault();
const direction = event.deltaY > 0 ? -1 : 1;
const step = event.shiftKey ? 5 : 15;
setLayoutScale(current => Math.min(800, Math.max(10, (Number(current) || 100) + direction * step)));
setLayoutScale(current => clampLayoutScale((current ?? fitScalePercent) + direction * step, fitScalePercent));
};
const handleSvgLoad = (event) => {
const image = event.currentTarget;
if (image.naturalWidth > 0 && image.naturalHeight > 0) {
setSvgSize({ width: image.naturalWidth, height: image.naturalHeight });
}
};
return (
@@ -2273,9 +2322,9 @@ Organization : OptiHK Limited
Scale
<input
type="range"
min="10"
max="800"
step="5"
min={minLayoutScale}
max={maxLayoutScale}
step="0.01"
value={normalizedScale}
onChange={(event) => updateScale(event.target.value)}
aria-label="Layout SVG preview scale"
@@ -2284,9 +2333,9 @@ Organization : OptiHK Limited
<label>
<input
type="number"
min="10"
max="800"
step="5"
min={minLayoutScale}
max={maxLayoutScale}
step="0.01"
value={normalizedScale}
onChange={(event) => updateScale(event.target.value)}
aria-label="Layout SVG preview scale percent"
@@ -2294,7 +2343,7 @@ Organization : OptiHK Limited
%
</label>
</div>
<div className="layout-preview-canvas" onWheel={handleWheel}>
<div className="layout-preview-canvas" ref={previewCanvasRef} onWheel={handleWheel}>
<div className="layout-preview-scroll-area">
<div
className="layout-preview-stage"
@@ -2304,6 +2353,7 @@ Organization : OptiHK Limited
className="layout-preview-image"
src={page.svgUrl}
alt={`${page.name} layout preview`}
onLoad={handleSvgLoad}
style={{ objectFit: 'contain' }}
/>
</div>