SVGcode: una PWA per convertire le immagini raster in grafica vettoriale SVG

SVGcode è un'app web progressiva che ti consente di convertire immagini raster, come JPG, PNG, GIF, WebP, AVIF e così via, in grafiche vettoriali in formato SVG. Utilizza l'API File System Access, l'API Async Clipboard, l'API File handling e la personalizzazione dell'overlay dei controlli delle finestre.

di Gemini Advanced.
Se preferisci la visione piuttosto che la lettura, questo articolo è disponibile anche come video.

Da raster a vettoriale

Hai mai ridimensionato un'immagine e il risultato era pixelato e deludente? Se quindi probabilmente hai usato un formato di immagine raster come WebP, PNG o JPG.

Se ingrandisci un'immagine raster, quest'ultima risulterà sgranata.

Al contrario, le grafiche vettoriali sono immagini definite da punti in un sistema di coordinate. Questi i punti sono collegati da linee e curve per formare poligoni e altre forme. La grafica vettoriale ha rispetto alle grafiche raster in quanto possono essere ridimensionate o ridotte a qualsiasi risoluzione senza pixelazione.

Ridimensionamento di un'immagine vettoriale senza perdita di qualità.

Introduzione al codice SVG

Ho creato una PWA chiamata SVGcode che può aiutarti a convertire le immagini raster in vettori di rete. Credito a cui è dovuto: non l'ho inventato io. Grazie al codice SVG, mi posiziono sulla uno strumento a riga di comando chiamato Potrace Peter Selinger che ho convertiti in Web Assembly, per poter essere usati in una App web.

Screenshot dell'applicazione SVGcode.
. L'app SVGcode.

Utilizzo del codice SVG

Innanzitutto, voglio mostrarti come utilizzare l'app. Inizio con l'immagine teaser per il Chrome Dev Summit che ho scaricato dal canale Twitter ChromiumDev. Questa è un'immagine raster PNG che poi e trascinalo nell'app SVGcode. Quando trascino il file, l'app traccia l'immagine colore per colore, finché non viene visualizzata una versione vettoriale dell'input. Ora posso ingrandire l'immagine e, come puoi vedere, i bordi rimangono nitidi. Ma aumentando lo zoom sul logo di Chrome, puoi notare che il tracciamento non è perfetta, in particolare i contorni del logo sembrano un po' maculati. Posso migliorare il risultato eliminazione delle macchie lunghe fino a cinque pixel.

Conversione di un'immagine rilasciata in SVG.

Posterizzazione nel codice SVG

Un passaggio importante per la vettorializzazione, in particolare per le immagini fotografiche, è la posterizzazione dell'input per ridurre il numero di colori. Il codice SVG mi consente di eseguire questa operazione per ogni canale colore e controllare con il risultato SVG mentre apporto modifiche. Quando il risultato è soddisfacente, posso salvare il file SVG sul mio disco rigido e la uso dove voglio.

Poster di un'immagine per ridurre il numero di colori.

API utilizzate nel codice SVG

Ora che hai visto di cosa è capace l'app, vediamo alcune delle API che contribuiscono a che avvenga la magia.

App web progressiva

SVGcode è un'app web progressiva installabile, pertanto è completamente offline. L'app è basata il Modello JS Vanilla per Vite.js e utilizza il popolare plug-in PWA Vite, che crea un service worker che che utilizza Workbox.js in background. 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 per il caso d'uso di SVGcode è ottimo.

Overlay controlli finestra

Per massimizzare lo spazio disponibile sullo schermo, il codice SVG usa Personalizzazione dell'overlay dei controlli finestra spostando il menu principale verso l'alto nell'area della barra del titolo. Puoi vedere che questa impostazione viene attivata al termine del flusso di installazione.

Installazione del codice SVG e attivazione della personalizzazione dell'overlay dei controlli delle finestre.

API File System Access

Per aprire i file immagine di input e salvare gli SVG risultanti, uso API File System Access. Questo mi consente di tenere un riferimento a ha aperto i file e continuare da dove avevo interrotto, anche dopo aver ricaricato l'app. Ogni volta che un'immagine viene salvato, viene ottimizzato tramite la libreria svgo. L'operazione potrebbe richiedere alcuni istanti. a seconda della complessità del file SVG. Per visualizzare la finestra di dialogo Salva file è necessario un gesto dell'utente. È quindi è importante ottenere l'handle del file prima che venga eseguita l'ottimizzazione SVG, quindi l'utente il gesto non viene invalidato nel momento in cui l'SVG ottimizzato è pronto.

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 usare la funzione di apertura file o, come avete visto sopra, trascina un file immagine nell'app. La funzionalità di apertura dei file è piuttosto semplice, è il caso di trascinamento. L'aspetto particolarmente interessante è che puoi ottenere un handle di file system dall'elemento Data Transfer tramite 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 ulteriori dettagli, consulta l'articolo sull'API File System Access e se ti interessa, studia il codice sorgente del codice SVG in src/js/filesystem.js

API Async Clipboard

Il codice SVGcode è inoltre completamente integrato negli appunti del sistema operativo tramite l'API Async Clipboard. Puoi incollare nell'app immagini da Esplora file del sistema operativo facendo clic sul incolla immagine o premi Comando o Ctrl più V sulla tastiera.

Incollamento di un'immagine da Esplora file nel codice SVG.

Di recente, l'API Async Clipboard ha acquisito la capacità di gestire anche le immagini SVG, copia un'immagine SVG e incollala in un'altra applicazione per un'ulteriore elaborazione.

Copia di un'immagine dal codice SVG a SVGOMG.
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 asincroni o esamina il file src/js/clipboard.js

Gestione di file

Una delle mie funzionalità preferite di SVGcode è la perfetta integrazione con il sistema operativo. Come PWA installato, può diventare un gestore di file, o persino il gestore di file predefinito, per i file immagine. Questo significa che quando mi trovo nel Finder sul mio computer macOS, posso fare clic con il tasto destro del mouse su un'immagine e aprirla con il codice SVG. Questa funzionalità è denominata Gestione file e funziona in base alla proprietà file_handlers nella il file manifest dell'app web e la coda di avvio, che consente all'app di consumare il file passato.

Apertura di un file dal desktop con l'app SVGcode installata.
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 maggiori informazioni, vedi Consentire alle applicazioni web installate di essere gestori di file e visualizzare 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 di volere per apportare modifiche a un file SVG creato con SVGcode, un modo per risolvere il problema è salvare il file, avvia l'app di modifica SVG e apri il file SVG da lì. Un flusso più fluido, però, consiste nel Utilizzare l'API Web Share, che consente di condividere direttamente i file. Quindi 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);
      }
    }
  }
});
Condivisione di un'immagine SVG in Gmail.

Target condivisione web (file)

D'altra parte, il codice SVGcode può anche fungere da destinazione per la condivisione e ricevere file da altre app. A far funzionare questa operazione, l'app deve comunicare al sistema operativo tramite API Web Share Target quali tipi di dati 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 in realtà non esiste, ma viene gestita esclusivamente nel campo fetch del service worker che poi passa 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);
      })(),
    );
  }
});
Condivisione di uno screenshot con il codice SVG.

Conclusione

Abbiamo fatto un rapido tour di alcune delle funzionalità avanzate dell'app nel codice SVG. Spero che questa app può diventare uno strumento essenziale per le tue esigenze di elaborazione delle immagini, insieme ad altre fantastiche app come Squoosh o SVGOMG.

Il codice SVG è disponibile all'indirizzo svgco.de. Hai visto cosa ho fatto lì? Puoi esamina il suo codice sorgente su GitHub. Nota che, poiché Potrace è Licenza concessa da GPL, così come il codice SVG. E con questo, felice vettore! Spero che il codice SVGcode ti sia utile, alcune delle sue funzionalità possono essere fonte di ispirazione per la tua prossima app.

Ringraziamenti

Questo articolo è stato esaminato da Joe Medley.