From 6a04b5feecdc1e3f8f566a531528a2ae0744d7b2 Mon Sep 17 00:00:00 2001 From: izawartka <59137928+izawartka@users.noreply.github.com> Date: Sun, 17 Aug 2025 17:49:13 +0200 Subject: [PATCH] Added export trimming *Objects in the exported SVG are now removed if they lay outside of the view --- src/components/map/Map.css | 6 +++++ src/helpers/exportHelper.js | 48 ++++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/components/map/Map.css b/src/components/map/Map.css index f442a23..19a3566 100644 --- a/src/components/map/Map.css +++ b/src/components/map/Map.css @@ -6,6 +6,12 @@ background-color: var(--map-background-color); } +.export-container { + position: relative; + width: 100%; + height: 100%; +} + .map svg path.track { stroke-width: 1.44px; fill: transparent; diff --git a/src/helpers/exportHelper.js b/src/helpers/exportHelper.js index 81b023b..688d81e 100644 --- a/src/helpers/exportHelper.js +++ b/src/helpers/exportHelper.js @@ -53,6 +53,44 @@ function processExportNode(svgNode, currentGroupStyles = {}) { } } +function checkNodeVisible(svgNode, trimClientRect) { + if(!svgNode) return false; + + if (svgNode.children?.length > 0) { + const visibleNodes = Array.from(svgNode.children).filter(child => { + return checkNodeVisible(child, trimClientRect); + }); + + return visibleNodes.length > 0; + } + + const clientRect = svgNode.getBoundingClientRect(); + if (clientRect.x + clientRect.width < trimClientRect.x) return false; + if (clientRect.x > trimClientRect.x + trimClientRect.width) return false; + if (clientRect.y + clientRect.height < trimClientRect.y) return false; + if (clientRect.y > trimClientRect.y + trimClientRect.height) return false; + if (clientRect.width === 0 || clientRect.height === 0) return false; + + return true; +} + +function removeNonVisibleObjects(svg) { + const sceneryRoot = svg.querySelector('g.scenery-root'); + if (!sceneryRoot) return; + + const trimClientRect = svg.getBoundingClientRect(); + + for (const layer of sceneryRoot.children) { + const objectsToDelete = Array.from(layer.children).filter(child => { + return !checkNodeVisible(child, trimClientRect); + }); + + for (const node of objectsToDelete) { + layer.removeChild(node); + } + } +} + function serializeAndDownload(svgNode, filename) { const serializer = new XMLSerializer(); const svgString = serializer.serializeToString(svgNode); @@ -74,17 +112,21 @@ function exportSvg(filename) { const svgOrig = mapElement.querySelector('svg'); if (!svgOrig) return false; + const exportContainer = document.createElement('div'); + exportContainer.classList.add('export-container', 'light-mode'); + const svg = svgOrig.cloneNode(true); - mapElement.appendChild(svg); + mapElement.appendChild(exportContainer); + exportContainer.appendChild(svg); svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); svg.setAttribute('version', '1.1'); - svg.classList.add('light-mode'); processExportNode(svg); + removeNonVisibleObjects(svg); serializeAndDownload(svg, filename); - mapElement.removeChild(svg); + mapElement.removeChild(exportContainer); return true; }