SVGcode è un'app web progressiva che consente di convertire immagini raster come JPG, PNG, GIF, WebP, AVIF e così via in grafica vettoriale in formato SVG. Utilizza l'API File System Access, l'API Async Clipboard, l'API File Handling e la personalizzazione di Window Controls Overlay.
Da raster a vettoriale
Hai mai ridimensionato un'immagine e il risultato è stato sfocato e insoddisfacente? Se così è, probabilmente hai utilizzato un formato di immagine raster come WebP, PNG o JPG.
Al contrario, le immagini vettoriali sono immagini definite da punti in un sistema di coordinate. Questi punti sono collegati da linee e curve per formare poligoni e altre forme. Le immagini vettoriali hanno un vantaggio rispetto alle immagini raster in quanto possono essere ridimensionate in base a qualsiasi risoluzione senza pixelizzazione.
Introduzione a SVGcode
Ho creato una PWA chiamata SVGcode che può aiutarti a convertire le immagini raster in vettori. Credito a cui è dovuto: non l'ho inventato io. Con SVGcode, mi appoggio su uno strumento a riga di comando chiamato Potrace di Peter Selinger che ho convertito in Web Assembly, in modo che possa essere utilizzato in un'app web.
Utilizzo di SVGcode
Innanzitutto, voglio mostrarti come utilizzare l'app. Comincio con l'immagine teaser per il Chrome Dev Summit che ho scaricato dal canale Twitter di ChromiumDev. Si tratta di un'immagine raster PNG che poi trascino nell'app SVGcode. Quando rilascio il file, l'app traccia l'immagine colore per colore, fino a quando non viene visualizzata una versione vettorizzata dell'input. Ora posso aumentare lo zoom dell'immagine e, come puoi vedere, i bordi rimangono nitidi. Tuttavia, se aumenti lo zoom sul logo di Chrome, puoi vedere che il rilevamento non è stato perfetto e, in particolare, i contorni del logo sembrano un po' macchiati. Posso migliorare il risultato eliminando gli aloni dal rilevamento sopprimendo gli aloni fino a, ad esempio, cinque pixel.
Posterizzazione in SVGcode
Un passaggio importante per la vettorizzazione, in particolare per le immagini fotografiche, è la posterizzazione dell'immagine di input per ridurre il numero di colori. SVGcode mi consente di farlo per canale di colore e di visualizzare l'SVG risultante man mano che apporto modifiche. Quando il risultato mi soddisfa, posso salvare il file SVG sul mio disco rigido e utilizzarlo dove preferisco.
API utilizzate in SVGcode
Ora che hai visto di cosa è capace l'app, ti mostro alcune delle API che contribuiscono a creare la magia.
App web progressiva
SVGcode è un'app web progressiva installabile e quindi completamente offline. L'app si basa sul modello Vanilla JS per Vite.js e utilizza il popolare plug-in PWA di Vite, che crea un servizio worker che utilizza Workbox.js sotto il cofano. Workbox è un insieme di librerie che possono supportare un service worker pronto per la produzione per le app web progressive. Questo pattern potrebbe non funzionare necessariamente per tutte le app, ma è perfetto per il caso d'uso di SVGcode.
Overlay controlli finestra
Per massimizzare lo spazio sullo schermo disponibile, SVGcode utilizza la personalizzazione di Window Controls Overlay spostando il menu principale nell'area della barra del titolo. Puoi vedere che questa impostazione viene attivata al termine del flusso di installazione.
API File System Access
Per aprire i file immagine di input e salvare gli SVG risultanti, utilizzo l'API File System Access. In questo modo posso mantenere un riferimento ai file aperti in precedenza e continuare da dove avevo interrotto, anche dopo il ricaricamento di un'app. Ogni volta che un'immagine viene salvata, viene ottimizzata tramite la libreria svgo. L'operazione potrebbe richiedere alcuni istanti, a seconda della complessità del file SVG. La visualizzazione della finestra di dialogo di salvataggio del file richiede un gesto dell'utente. È quindi importante ottenere l'handle del file prima dell'ottimizzazione dell'SVG, in modo che il gesto dell'utente non venga invalidato al momento del completamento dell'SVG ottimizzato.
try {
let svg = svgOutput.innerHTML;
let handle = null;
// To not consume the user gesture obtain the handle before preparing the
// blob, which may take longer.
if (supported) {
handle = await showSaveFilePicker({
types: [{description: 'SVG file', accept: {'image/svg+xml': ['.svg']}}],
});
}
showToast(i18n.t('optimizingSVG'), Infinity);
svg = await optimizeSVG(svg);
showToast(i18n.t('savedSVG'));
const blob = new Blob([svg], {type: 'image/svg+xml'});
await fileSave(blob, {description: 'SVG file'}, handle);
} catch (err) {
console.error(err.name, err.message);
showToast(err.message);
}
Trascina
Per aprire un'immagine di input, posso utilizzare la funzione di apertura file o, come hai visto sopra,
trascinare un file immagine nell'app. La funzionalità di apertura file è piuttosto semplice,
più interessante è la funzionalità di trascinamento. La cosa particolarmente interessante è che puoi recuperare un handle del file system dall'elemento di trasferimento dati tramite il metodo getAsFileSystemHandle()
. Come accennato in precedenza, posso mantenere questo handle, in modo che sia pronto quando l'app viene ricaricata.
document.addEventListener('drop', async (event) => {
event.preventDefault();
dropContainer.classList.remove('dropenter');
const item = event.dataTransfer.items[0];
if (item.kind === 'file') {
inputImage.addEventListener(
'load',
() => {
URL.revokeObjectURL(blobURL);
},
{once: true},
);
const handle = await item.getAsFileSystemHandle();
if (handle.kind !== 'file') {
return;
}
const file = await handle.getFile();
const blobURL = URL.createObjectURL(file);
inputImage.src = blobURL;
await set(FILE_HANDLE, handle);
}
});
Per maggiori dettagli, consulta l'articolo sull'API File System Access e,
se ti interessa, esamina il codice sorgente del codice SVG in
src/js/filesystem.js
.
API Async Clipboard
SVGcode è inoltre completamente integrato con gli appunti del sistema operativo tramite l'API Async Clipboard. Puoi incollare le immagini dall'esploratore file del sistema operativo nell'app facendo clic sul pulsante Incolla immagine o premendo Comando o Controllo + V sulla tastiera.
Di recente l'API Async Clipboard ha acquisito la capacità di gestire anche le immagini SVG, quindi puoi anche copiare un'immagine SVG e incollarla in un'altra applicazione per ulteriori elaborazioni.
copyButton.addEventListener('click', async () => {
let svg = svgOutput.innerHTML;
showToast(i18n.t('optimizingSVG'), Infinity);
svg = await optimizeSVG(svg);
const textBlob = new Blob([svg], {type: 'text/plain'});
const svgBlob = new Blob([svg], {type: 'image/svg+xml'});
navigator.clipboard.write([
new ClipboardItem({
[svgBlob.type]: svgBlob,
[textBlob.type]: textBlob,
}),
]);
showToast(i18n.t('copiedSVG'));
});
Per scoprire di più, leggi l'articolo Appunti sincronici o consulta il file
src/js/clipboard.js
.
Gestione dei file
Una delle mie funzionalità preferite di SVGcode è la sua perfetta integrazione con il sistema operativo. Come PWA installata, può diventare un gestore di file o addirittura il gestore di file predefinito per i file immagine. Ciò significa che quando sono nel Finder sul mio computer macOS, posso fare clic con il tasto destro del mouse su un'immagine e aprirla con SVGcode. Questa funzionalità è chiamata Gestione file e funziona in base alla proprietà file_handlers nel manifest dell'app web e nella coda di lancio, che consente all'app di utilizzare il file passato.
window.launchQueue.setConsumer(async (launchParams) => {
if (!launchParams.files.length) {
return;
}
for (const handle of launchParams.files) {
const file = await handle.getFile();
if (file.type.startsWith('image/')) {
const blobURL = URL.createObjectURL(file);
inputImage.addEventListener(
'load',
() => {
URL.revokeObjectURL(blobURL);
},
{once: true},
);
inputImage.src = blobURL;
await set(FILE_HANDLE, handle);
return;
}
}
});
Per ulteriori informazioni, vedi Consentire alle applicazioni web installate di essere gestori di file e visualizza il codice sorgente in
src/js/filehandling.js
.
Condivisione web (File)
Un altro esempio di integrazione con il sistema operativo è la funzionalità di condivisione dell'app. Supponendo che voglia apportare modifiche a un file SVG creato con SVGcode, un modo per risolvere il problema è salvare il file, avviare l'app di modifica SVG e aprirlo da lì. Un flusso più semplice, però, è utilizzare l'API Web Share, che consente di condividere direttamente i file. Pertanto, se l'app di modifica SVG è una destinazione di condivisione, può ricevere direttamente il file senza deviazioni.
shareSVGButton.addEventListener('click', async () => {
let svg = svgOutput.innerHTML;
svg = await optimizeSVG(svg);
const suggestedFileName =
getSuggestedFileName(await get(FILE_HANDLE)) || 'Untitled.svg';
const file = new File([svg], suggestedFileName, { type: 'image/svg+xml' });
const data = {
files: [file],
};
if (navigator.canShare(data)) {
try {
await navigator.share(data);
} catch (err) {
if (err.name !== 'AbortError') {
console.error(err.name, err.message);
}
}
}
});
Destinazione condivisione web (file)
D'altra parte, il codice SVGcode può anche fungere da destinazione per la condivisione e ricevere file da altre app. Per far funzionare questa operazione, l'app deve comunicare al sistema operativo tramite l'API Web Share Target i tipi di dati che può accettare. Ciò avviene tramite un campo dedicato nel file manifest dell'app web.
{
"share_target": {
"action": "https://svgco.de/share-target/",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"files": [
{
"name": "image",
"accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]
}
]
}
}
}
La route action
non esiste realmente, ma viene gestita esclusivamente nell'handler fetch
del service worker, che poi trasmette i file ricevuti per l'elaborazione effettiva nell'app.
self.addEventListener('fetch', (fetchEvent) => {
if (
fetchEvent.request.url.endsWith('/share-target/') &&
fetchEvent.request.method === 'POST'
) {
return fetchEvent.respondWith(
(async () => {
const formData = await fetchEvent.request.formData();
const image = formData.get('image');
const keys = await caches.keys();
const mediaCache = await caches.open(
keys.filter((key) => key.startsWith('media'))[0],
);
await mediaCache.put('shared-image', new Response(image));
return Response.redirect('./?share-target', 303);
})(),
);
}
});
Conclusione
Bene, questo è stato un breve tour di alcune delle funzionalità avanzate dell'app in SVGcode. Spero che questa app possa diventare uno strumento essenziale per le tue esigenze di elaborazione delle immagini, insieme ad altre app straordinarie come Squoosh o SVGOMG.
Il codice SVG è disponibile all'indirizzo svgco.de. Hai capito cosa ho fatto? Puoi esaminare il codice sorgente su GitHub. Tieni presente che, poiché Potrace è concesso in licenza GPL, lo è anche SVGcode. Buona trasformazione in vettori. Spero che SVGcode ti sia utile e che alcune delle sue funzionalità possano essere d'ispirazione per la tua prossima app.
Ringraziamenti
Questo articolo è stato esaminato da Joe Medley.