SVGcode: PWA untuk mengonversi gambar raster menjadi grafik vektor SVG

SVGcode adalah Progressive Web App yang memungkinkan Anda mengonversi gambar raster seperti JPG, PNG, GIF, WebP, AVIF, dll. ke grafik vektor dalam format SVG. Fitur ini menggunakan File System Access API, Asynchronous Clipboard API, File Handling API, dan penyesuaian Window Controls Overlay.

(Jika Anda lebih suka menonton daripada membaca, artikel ini juga tersedia sebagai video.)

Dari raster ke vektor

Pernahkah Anda menskalakan sebuah gambar dan hasilnya terlihat pecah dan tidak memuaskan? Jika jadi, Anda mungkin pernah menangani format gambar raster seperti WebP, PNG, atau JPG.

Peningkatan skala gambar raster akan membuatnya terlihat pecah.

Sebaliknya, grafik vektor adalah gambar yang didefinisikan oleh titik-titik dalam sistem koordinat. Ini titik-titik dihubungkan oleh garis dan kurva untuk membentuk poligon dan bentuk lainnya. Grafik vektor memiliki keunggulan dibandingkan grafis raster karena skalanya dapat ditingkatkan atau diturunkan ke resolusi apa pun tanpa pikselasi.

Menskalakan gambar vektor tanpa kehilangan kualitas.

Memperkenalkan SVGcode

Saya telah membuat PWA bernama SVGcode yang dapat membantu Anda mengonversi gambar raster menjadi vektor. Kredit untuk jatuh tempo: Saya tidak membuat ini. Dengan SVGcode, saya dapat berdiri di bahu alat command line yang disebut Potrace dengan Peter Selinger yang saya miliki dikonversi ke Web Assembly, sehingga dapat digunakan dalam Aplikasi web.

screenshot aplikasi SVGcode.
Aplikasi SVGcode.

Menggunakan SVGcode

Pertama, saya ingin menunjukkan cara menggunakan aplikasi ini. Saya memulai dengan gambar teaser untuk Chrome Dev Summit yang saya unduh dari saluran Twitter ChromiumDev. Ini adalah gambar raster PNG yang kemudian saya tarik ke aplikasi SVGcode. Ketika saya melepaskan file, aplikasi akan melacak warna gambar berdasarkan warna, hingga versi input vektorisasi muncul. Sekarang saya dapat memperbesar gambar, dan seperti yang Anda lihat, tepinya tetap tajam. Namun dengan memperbesar logo Chrome, Anda dapat melihat bahwa pelacakan sempurna, dan terutama bagian tepi logo terlihat sedikit berbintik-bintik. saya dapat meningkatkan hasilnya dengan menghilangkan bintik pelacakan dengan mengurangi bintik-bintik hingga, misalnya, lima piksel.

Mengonversi gambar yang dilepas ke SVG.

Posterisasi dalam SVGcode

Langkah penting untuk vektorisasi, terutama untuk gambar fotografi, adalah posterisasi input untuk mengurangi jumlah warna. SVGcode memungkinkan saya untuk melakukan ini per saluran warna, dan melihat SVG yang dihasilkan saat saya membuat perubahan. Jika hasilnya memuaskan, saya dapat menyimpan SVG ke dalam {i>hard disk<i} dan menggunakannya di mana pun saya mau.

Membuat poster gambar untuk mengurangi jumlah warna.

API yang digunakan dalam SVGcode

Setelah Anda mengetahui kemampuan aplikasi ini, saya akan menunjukkan beberapa API yang membantu keajaiban itu terjadi.

Progressive Web App

SVGcode adalah Progressive Web App yang dapat diinstal dan karenanya diaktifkan sepenuhnya secara offline. Aplikasi ini didasarkan di Template JS Vanilla untuk Vite.js dan menggunakan PWA plugin Vite, yang membuat pekerja layanan yang menggunakan Workbox.js di balik layar. {i>Workbox<i} adalah kumpulan library yang dapat mendukung pekerja layanan siap produksi untuk Progressive Web App, Pola ini mungkin tidak berfungsi untuk semua aplikasi, namun ini bagus untuk kasus penggunaan SVGcode.

Overlay Kontrol Jendela

Untuk memaksimalkan ruang layar yang tersedia, SVGcode menggunakan Penyesuaian Overlay Kontrol Jendela dengan memindahkan menu utamanya ke atas ke dalam area bilah judul. Anda dapat melihat proses ini diaktifkan di akhir alur penginstalan.

Menginstal SVGcode dan mengaktifkan penyesuaian Window Controls Overlay.

File System Access API

Untuk membuka file gambar input dan menyimpan SVG yang dihasilkan, saya menggunakan API Akses Sistem File. Hal ini memungkinkan saya untuk menyimpan referensi ke data sebelumnya membuka file dan melanjutkan dari tempat yang saya tinggalkan, bahkan setelah aplikasi dimuat ulang. Setiap kali gambar ditampilkan disimpan, dioptimalkan melalui library svgo, yang mungkin memerlukan waktu beberapa saat, tergantung pada kompleksitas SVG. Menampilkan dialog penyimpanan file memerlukan gestur pengguna. Penting Oleh karena itu, penting untuk mendapatkan handle file sebelum pengoptimalan SVG terjadi, sehingga pengguna tidak dibatalkan pada saat SVG yang dioptimalkan sudah siap.

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

Tarik lalu lepas

Untuk membuka gambar {i>input<i}, saya dapat menggunakan fitur buka {i>file<i}, atau, seperti yang Anda lihat di atas, menarik lalu melepas file gambar ke aplikasi. Fitur untuk membuka {i>file<i} cukup mudah, lebih yang menarik adalah {i> drag and drop<i}. Yang sangat menarik dari hal ini adalah Anda dapat mendapatkan handle sistem file dari item transfer data melalui getAsFileSystemHandle() . Seperti yang disebutkan sebelumnya, saya dapat mempertahankan handle ini, sehingga sudah siap saat aplikasi dimuat ulang.

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

Untuk detail selengkapnya, lihat artikel tentang File System Access API dan, jika Anda tertarik, pelajari kode sumber SVGcode di src/js/filesystem.js

Async Clipboard API

SVGcode juga terintegrasi penuh dengan papan klip sistem operasi melalui Asynchronous Clipboard API. Anda dapat menempelkan gambar dari file explorer sistem operasi ke dalam aplikasi dengan mengklik tempel tombol gambar atau dengan menekan command atau control plus v pada keyboard Anda.

Menempelkan gambar dari file explorer ke SVGcode.

Asynchronous Clipboard API baru-baru ini juga memperoleh kemampuan untuk menangani gambar SVG, sehingga Anda dapat salin gambar SVG dan tempelkan ke aplikasi lain untuk diproses lebih lanjut.

Menyalin gambar dari SVGcode ke 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'));
});

Untuk mempelajari lebih lanjut, baca artikel Papan Klip Asinkron, atau lihat file-nya src/js/clipboard.js

Penanganan File

Salah satu fitur favorit saya dari SVGcode adalah betapa baik menyatu dengan sistem operasi. Sebagai seorang PWA yang diinstal, dapat menjadi pengendali file, atau bahkan pengendali file default, untuk file gambar. Ini berarti bahwa ketika saya berada di {i>Finder<i} di komputer macOS, saya dapat mengklik kanan suatu gambar dan membukanya dengan SVGcode. Fitur ini disebut Penanganan File dan bekerja berdasarkan properti file_handlers di Manifes Aplikasi Web dan antrean peluncuran, yang memungkinkan aplikasi menggunakan file yang diteruskan.

Membuka file dari desktop yang berisi aplikasi SVGcode yang terinstal.
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;
    }
  }
});

Untuk informasi selengkapnya, lihat Mengizinkan aplikasi web yang terinstal menjadi handler file, dan melihat kode sumber di src/js/filehandling.js

Berbagi Web (File)

Contoh lain dari perpaduan dengan sistem operasi adalah fitur berbagi aplikasi. Dengan asumsi saya ingin mengedit SVG yang dibuat dengan SVGcode, salah satu cara untuk menanganinya adalah dengan menyimpan file, luncurkan aplikasi pengeditan SVG, lalu buka file SVG dari sana. Namun, alur yang lebih lancar adalah menggunakan Web Share API, yang memungkinkan file dibagikan secara langsung. Jadi jika aplikasi pengeditan SVG adalah target berbagi, aplikasi ini dapat langsung menerima file tanpa penyimpangan.

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);
      }
    }
  }
});
Membagikan gambar SVG ke Gmail.

Target Berbagi Web (File)

Sebaliknya, SVGcode juga dapat bertindak sebagai target berbagi dan menerima file dari aplikasi lain. Kepada membuatnya berfungsi, aplikasi harus memberi tahu sistem operasi melalui Web Share Target API jenis data yang dapat diterima. Hal ini dilakukan melalui bidang khusus di Manifes Aplikasi 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"]
        }
      ]
    }
  }
}

Rute action sebenarnya tidak ada, tetapi ditangani hanya di fetch pekerja layanan yang kemudian meneruskan file yang diterima untuk pemrosesan aktual di aplikasi.

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);
      })(),
    );
  }
});
Membagikan screenshot ke SVGcode.

Kesimpulan

Baiklah, ini adalah tur singkat tentang beberapa fitur aplikasi lanjutan di SVGcode. Semoga aplikasi ini dapat menjadi alat penting untuk kebutuhan pemrosesan gambar Anda bersama dengan aplikasi hebat lainnya seperti Squoosh atau SVGOMG.

SVGcode tersedia di svgco.de. Lihat apa yang aku lakukan di sana? Anda dapat meninjau kode sumbernya di GitHub. Perhatikan bahwa karena Potrace adalah berlisensi GPL, begitu juga SVGcode. Dan dengan itu, selamat membuat vektorisasi! Saya harap SVGcode bermanfaat, dan beberapa fiturnya dapat menginspirasi aplikasi Anda berikutnya.

Ucapan terima kasih

Artikel ini ditinjau oleh Joe Medley.