SVGcode: aplikacja PWA do konwertowania obrazów rastrowych na grafiki wektorowe SVG.

SVGcode to progresywna aplikacja internetowa, która umożliwia konwertowanie obrazów rastrowych, takich jak JPG, PNG, GIF, WebP, AVIF itp., na grafiki wektorowe w formacie SVG. Wykorzystuje interfejsy File System Access API, Async Clipboard API, API obsługi plików i dostosowanie nakładki kontroli okien.

(Jeśli wolisz oglądać niż czytać, ten artykuł jest też dostępny w formie filmu).

Z rastera na wektor

Czy zdarzyło Ci się przeskalować obraz, który był rozpikselowany i nie był zadowalający? Jeśli tak, prawdopodobnie pracowałeś/pracowałaś z obrazem rastrowym w formacie WebP, PNG lub JPG.

Powiększenie obrazu rastrowego powoduje, że staje się on pikselowaty.

Grafika wektorowa to natomiast obrazy zdefiniowane przez punkty w układzie współrzędnych. Punkty te są połączone liniami i krzywiznami, tworząc wielokąty i inne kształty. Grafika wektorowa ma tę zaletę nad grafiką rastrową, że można ją powiększyć lub zmniejszyć do dowolnej rozdzielczości bez utraty jakości.

Zwiększanie obrazu wektorowego bez utraty jakości.

Przedstawiamy SVGcode

Mam aplikację PWA o nazwie SVGcode, która pomaga konwertować obrazy rastrowe na wektorowe. Odpowiednie przypisywanie udziału w konwersji: nie ja wymyśliłem tego rozwiązania. W przypadku SVGcode korzystam z narzędzi wiersza poleceń o nazwie Potrace autorstwa Petera Selingera, które przekształciłem w Web Assembly, aby można było go używać w aplikacji internetowej.

Zrzut ekranu aplikacji SVGcode
Aplikacja SVGcode.

Za pomocą SVGcode

Najpierw pokażę, jak korzystać z tej aplikacji. Zacznę od grafiki zwiastuna Chrome Dev Summit pobranego z kanału ChromiumDev na Twitterze. To obraz rastrowy PNG, który przeciągam do aplikacji SVGcode. Gdy upuszczam plik, aplikacja śledzi jego kolor według koloru, aż pojawi się wektorowa wersja danych wejściowych. Teraz mogę powiększyć obraz i jak widzisz, krawędzie pozostają ostre. Po zbliżeniu logo Chrome widać jednak, że śledzenie nie było idealne, a zwłaszcza kontury logo wyglądają nieco nierówno. Mogę poprawić wynik, usuwając z wykresu plamki o rozmiary do 5 pikseli.

Konwertowanie przeciągniętego obrazu na SVG.

Posteryzacja w kodzie SVG

Ważnym krokiem w procesie wektoryzacji, zwłaszcza w przypadku obrazów fotograficznych, jest posteryzacja wejściowego obrazu w celu zmniejszenia liczby kolorów. SVGcode pozwala mi to robić w przypadku każdego kanału kolorów i oglądać wynikowy plik SVG w miarę wprowadzania zmian. Gdy uzyskam zadowalający efekt, mogę zapisać plik SVG na dysku twardym i używać go gdziekolwiek zechcę.

Posteryzacja obrazu w celu zmniejszenia liczby kolorów.

Interfejsy API używane w SVGcode

Skoro już wiesz, jakie możliwości oferuje aplikacja, pokażę Ci teraz kilka interfejsów API, dzięki którym można w niej to zrobić.

Progresywna aplikacja internetowa

SVGcode to instalowana progresywna aplikacja internetowa, która działa w trybie offline. Aplikacja opiera się na szablonie Vanilla JS dla Vite.js i korzysta z popularnej wtyczki VITE PWA, która tworzy usługę workera, która pod spodem korzysta z Workbox.js. Workbox to zbiór bibliotek, które mogą służyć do tworzenia skryptów service worker dostępnych w wersji produkcyjnej w progresywnych aplikacjach internetowych. Ten wzór może nie działać w przypadku wszystkich aplikacji, ale w przypadku SVGcode jest świetny.

Nakładka z elementami sterującymi okna

Aby zmaksymalizować dostępną przestrzeń na ekranie, SVGcode używa nakładki z elementami sterującymi okna, przenosząc główne menu do obszaru paska tytułu. Możesz zobaczyć, jak to się dzieje, na końcu procesu instalacji.

Instalowanie kodu SVG i aktywowanie funkcji dostosowywania nakładki elementów sterowania oknem.

File System Access API

Aby otwierać pliki obrazów wejściowych i zapisywać powstałe pliki SVG, używam interfejsu File System Access API. Pozwala mi to zachować odniesienie do wcześniej otwartych plików i kontynuować pracę w miejscu, w którym ją przerwałem, nawet po ponownym załadowaniu aplikacji. Za każdym razem, gdy obraz zostanie zapisany, jest optymalizowany za pomocą biblioteki svgo. Może to chwilę potrwać, w zależności od złożoności pliku SVG. Wyświetlenie okna zapisywania pliku wymaga gestu użytkownika. Dlatego ważne jest, aby uzyskać uchwyt pliku przed optymalizacją SVG, aby gest użytkownika nie został unieważniony do czasu, gdy gotowy będzie zoptymalizowany plik SVG.

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);
}

Przeciągnij i upuść

Aby otworzyć obraz wejściowy, mogę użyć funkcji otwierania plików lub, jak widać powyżej, przeciągnąć i upuścić plik obrazu w aplikacji. Funkcja otwierania plików jest dość prosta, ale ciekawsze jest przeciąganie i upuszczanie. Jest to szczególnie przydatne, ponieważ możesz uzyskać uchwyt systemu plików z elementu przesyłania danych za pomocą metody getAsFileSystemHandle(). Jak już wspomniałem, mogę zachować ten identyfikator, aby był gotowy, gdy aplikacja zostanie ponownie załadowana.

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);
  }
});

Więcej informacji znajdziesz w artykule o interfejsie File System Access API. Jeśli chcesz, możesz też zapoznać się ze źródłowym kodem SVG w pliku src/js/filesystem.js.

Async Clipboard API

SVGcode jest też w pełni zintegrowany ze schowkiem systemu operacyjnego za pomocą interfejsu Async Clipboard API. Obrazy z eksploratora plików systemu operacyjnego możesz wkleić do aplikacji, klikając przycisk wklejania obrazu lub naciskając Command lub Control + V na klawiaturze.

Wklejanie obrazu z Eksploratora plików do kodu SVG.

Interfejs Async Clipboard API został niedawno rozszerzony o obsługę obrazów SVG, dzięki czemu możesz kopiować obrazy SVG i wklejać je w innej aplikacji w celu dalszego przetwarzania.

Kopiowanie obrazu z kodu SVG do 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'));
});

Więcej informacji znajdziesz w artykule Schowek asynchroniczny lub pliku src/js/clipboard.js.

Obsługa plików

Jedną z moich ulubionych funkcji SVGcode jest to, jak dobrze komponuje się on z systemem operacyjnym. Jako zainstalowana aplikacja PWA może stać się modułem obsługi plików, a nawet domyślnym modułem obsługi plików graficznych. Oznacza to, że gdy jestem w Finderze na komputerze z systemem macOS, mogę kliknąć obraz prawym przyciskiem myszy i otworzyć go za pomocą SVGcode. Ta funkcja nosi nazwę obsługi plików i działa na podstawie właściwości file_handlers w pliku manifestu aplikacji internetowej i kole zadań, co pozwala aplikacji na wykorzystanie przekazanego pliku.

Otwieranie pliku z pulpitu przy zainstalowanej aplikacji SVGcode.
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;
    }
  }
});

Więcej informacji znajdziesz w sekcji Pozwalaj, aby zainstalowane aplikacje internetowe mogły działać jako moduły obsługi plików, i wyświetl kod źródłowy w src/js/filehandling.js.

Udostępnianie w internecie (pliki)

Innym przykładem dopasowania do systemu operacyjnego jest funkcja udostępniania w aplikacji. Załóżmy, że chcę wprowadzić zmiany w pliku SVG utworzonym za pomocą SVGcode. W tym celu mogę zapisać plik, uruchomić aplikację do edycji SVG, a potem otworzyć plik SVG. Płynniejszy proces to jednak użycie interfejsu Web Share API, który umożliwia bezpośrednie udostępnianie plików. Jeśli więc aplikacja do edycji SVG jest obiektem docelowym udostępniania, może bezpośrednio odbierać plik bez odchylenia.

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);
      }
    }
  }
});
Udostępnianie obrazu SVG w Gmailu.

Docelowy adres URL udostępniania w przeglądarce (pliki)

Odwrotnie, SVGcode może też działać jako docelowa aplikacja do udostępniania i odbierać pliki z innych aplikacji. Aby to działało, aplikacja musi poinformować system operacyjny za pomocą interfejsu Web Share Target API, jakie typy danych może akceptować. Odbywa się to przez specjalne pole w pliku manifestu aplikacji internetowej.

{
  "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"]
        }
      ]
    }
  }
}

Ścieżka action nie istnieje, ale jest obsługiwana wyłącznie w obiekcie fetch w ramach usługi workera, który następnie przekazuje otrzymane pliki do przetworzenia w aplikacji.

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);
      })(),
    );
  }
});
Udostępnianie zrzutu ekranu do SVGcode.

Podsumowanie

To była krótka prezentacja zaawansowanych funkcji aplikacji SVGcode. Mam nadzieję, że ta aplikacja stanie się podstawowym narzędziem do przetwarzania obrazów, tak jak w przypadku innych świetnych aplikacji, takich jak Squoosh czy SVGOMG.

SVGcode jest dostępny na stronie svgco.de. Widzisz, co zrobiłem? Kod źródłowy znajdziesz na GitHubie. Pamiętaj, że ponieważ Potrace jest objęty licencją GPL, tak samo jest w przypadku SVGcode. Życzymy udanej wektoryzacji. Mam nadzieję, że SVGcode okaże się przydatny, a jego funkcje zainspirują Cię do stworzenia kolejnej aplikacji.

Podziękowania

Ten artykuł został sprawdzony przez Joe Medley.