Uno dei vantaggi dei file SVG è la possibilità di modificarli con JavaScript in una pagina HTML. Per semplificare il processo creo una piccola app con Svelte in cui è possibile caricare un file SVG, modificarlo e poi salvarlo nuovamente su disco.
Aprire un file SVG con HTML
Per prima cosa creo un pulsante per selezionare il file da caricare e un elemento html dove mostrare il file
<script lang="ts">
import { tick } from "svelte";
let src: string;
let divSVG: HTMLDivElement;
const onFileSelected = async (e) => {
let file = e.target.files[0];
src = null;
await tick();
src = await file.text();
await tick();
const svg = divSVG.getElementsByTagName("svg")[0];
const viewBoxWidth = parseFloat(svg.getAttribute("width"));
const viewBoxHeight = parseFloat(svg.getAttribute("height"));
const viewBox = `0 0 ${viewBoxWidth} ${viewBoxHeight}`;
svg.setAttribute("viewBox", viewBox);
};
</script>
<main>
<input type="file" accept=".svg" on:change={(e) => onFileSelected(e)} />
{#if src}
<div class="svg" bind:this="{divSVG}" style:width="100vw" style:height="80vh">
{@html src}
</div>
{/if}
</main>
Dopo aver selezionato il file ottengo qualcosa simile a questo:

Come zoomare e muovere mappe SVG
Per alcune mappe può bastare una visuale complessiva ma in questo caso mi interessa poter zoomare su alcuni elementi. E, ovviamente, anche potermi muovere all’interno della mappa.
Per fare questo posso usare il codice del repository luncheon/svg-pan-zoom-container. Installo il pacchetto usando:
npm i svg-pan-zoom-container
Quindi modifico il mio codice:
<script lang="ts">
import "svg-pan-zoom-container";
</script>
<div
class="svg"
bind:this={divSVG}
style:width="100vw"
style:height="80vh"
data-zoom-on-wheel="min-scale: 0.3; max-scale: 20;"
data-pan-on-drag="button: left;modifier: Control"
>
{@html src}
</div>
L’attributo data-zoom-on-wheel
abilita lo zoom
con la rotellina del mouse. Imposto come scala minima 0.3 e come scala massima 20. L’attributo data-pan-on-drag
abilita lo spostamento con il mouse. In questo caso imposto che lo spostamento avviene usando la combinazione tasto control
più tasto sinistro del mouse
.
Oltre all rotellina del mouse è utile avere anche un pulsante o qualcosa di simile per gestire lo zoom. Posso, per esempio creare un elemento input
di tipo range
, collegando il suo valore alla variabile scale
<script>
let scale = 1;
</script>
<input type="range" min="0.3" max="20" step="1" bind:value="{scale}" />
Per passare la scala dalla mappa all’elemento aggiungo MutationObserver
:
import { getScale, setScale, resetScale } from "svg-pan-zoom-container";
let observer;
const onFileSelected = async (event) => {
// ...
observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
scale = getScale(divSVG);
});
});
observer.observe(divSVG.firstElementChild, {
attributes: true,
attributeFilter: ["transform"],
});
};
Aggiungo poi un nuovo evento all’elemento input[range]
per poter modificare lo zoom in maniera dinamica:
<input
type="range"
min="0.3"
max="20"
step="1"
bind:value={scale}
on:input={(e) => {
setScale(divSVG, scale, { minScale: 0.3, maxScale: 20 });
}}
/>
Il risultato è simile a questo:
Infine, posso aggiungere un pulsante per resettare la scala e tornare a quella originaria:
<button
on:click={(e) => {
resetScale(divSVG);
}}>Reset Scale</button
>