SVGcode: PWA для преобразования растровых изображений в векторную графику SVG.

SVGcode — это прогрессивное веб-приложение, которое позволяет конвертировать растровые изображения, такие как JPG, PNG, GIF, WebP, AVIF и т. д., в векторную графику в формате SVG. Он использует API доступа к файловой системе, API асинхронного буфера обмена, API обработки файлов и настройку наложения элементов управления окнами.

(Если вы предпочитаете смотреть, а не читать, эта статья также доступна в виде видео .)

От растра к вектору

Вы когда-нибудь масштабировали изображение, а результат был пиксельным и неудовлетворительным? Если да, то вы, вероятно, имели дело с такими форматами растровых изображений, как WebP, PNG или JPG.

Увеличение растрового изображения делает его пиксельным.

Напротив, векторная графика — это изображения, которые определяются точками в системе координат. Эти точки соединяются линиями и кривыми, образуя многоугольники и другие фигуры. Векторная графика имеет преимущество перед растровой графикой в ​​том, что ее можно масштабировать до любого разрешения без пикселизации.

Масштабирование векторного изображения без потери качества.

Представляем SVG-код

Я создал PWA под названием SVGcode , который поможет вам конвертировать растровые изображения в векторы. Надо отдать должное: не я это придумал. Используя SVGcode, я просто опираюсь на инструмент командной строки Potrace от Питера Селинджера , который я преобразовал в Web Assembly , чтобы его можно было использовать в веб-приложении.

Скриншот приложения SVGcode.
Приложение SVGcode .

Использование SVG-кода

Сначала я хочу показать вам, как использовать приложение. Начну с изображения-тизера Chrome Dev Summit, которое я скачал с канала ChromiumDev в Твиттере. Это растровое изображение PNG, которое я затем перетаскиваю в приложение SVGcode. Когда я перетаскиваю файл, приложение отслеживает цвет изображения по цвету, пока не появится векторизованная версия входных данных. Теперь я могу увеличить изображение, и, как видите, края остаются четкими. Но, увеличив логотип Chrome, можно увидеть, что обводка не идеальна, и особенно контуры логотипа выглядят немного крапчатыми. Я могу улучшить результат, убрав пятнышки на трассировке, подавив пятнышки размером, скажем, до пяти пикселей.

Преобразование переброшенного изображения в SVG.

Постеризация в SVGcode

Важным шагом при векторизации, особенно фотографических изображений, является постеризация входного изображения для уменьшения количества цветов. SVGcode позволяет мне делать это для каждого цветового канала и видеть полученный SVG по мере внесения изменений. Когда результат меня устраивает, я могу сохранить SVG на свой жесткий диск и использовать его где захочу.

Постеризация изображения для уменьшения количества цветов.

API, используемые в SVGcode

Теперь, когда вы увидели, на что способно приложение, позвольте мне показать вам некоторые API-интерфейсы, которые помогают творить чудеса.

Прогрессивное веб-приложение

SVGcode — это устанавливаемое прогрессивное веб-приложение, поэтому оно полностью доступно в автономном режиме. Приложение основано на шаблоне Vanilla JS для Vite.js и использует популярный плагин Vite PWA , который создает сервис-воркера, использующего Workbox.js под капотом. Workbox — это набор библиотек, которые могут обеспечить готовый к работе сервис-воркер для прогрессивных веб-приложений. Этот шаблон может не обязательно работать для всех приложений, но для варианта использования SVGcode он отлично подходит.

Наложение оконных элементов управления

Чтобы максимизировать доступное пространство экрана, SVGcode использует настройку наложения элементов управления окнами , перемещая главное меню вверх в область заголовка. Вы можете увидеть, как это активируется в конце процесса установки.

Установка SVGcode и активация настройки наложения элементов управления окнами.

API доступа к файловой системе

Чтобы открыть файлы входных изображений и сохранить полученные SVG, я использую API доступа к файловой системе . Это позволяет мне сохранить ссылку на ранее открытые файлы и продолжить с того места, где я остановился, даже после перезагрузки приложения. Всякий раз, когда изображение сохраняется, оно оптимизируется с помощью библиотеки svgo , что может занять некоторое время, в зависимости от сложности SVG. Для отображения диалогового окна сохранения файла требуется жест пользователя. Поэтому важно получить дескриптор файла до того, как произойдет оптимизация SVG, чтобы жест пользователя не стал недействительным к моменту готовности оптимизированного 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);
}

Перетащите

Чтобы открыть входное изображение, я могу либо использовать функцию открытия файла, либо, как вы видели выше, просто перетащить файл изображения в приложение. Функция открытия файла довольно проста, более интересным является случай перетаскивания. Что особенно приятно в этом, так это то, что вы можете получить дескриптор файловой системы из элемента передачи данных с помощью метода getAsFileSystemHandle() . Как упоминалось ранее, я могу сохранить этот дескриптор, чтобы он был готов к перезагрузке приложения.

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

Для получения более подробной информации ознакомьтесь со статьей об API доступа к файловой системе и, если вам интересно, изучите исходный код SVGcode в src/js/filesystem.js .

API асинхронного буфера обмена

SVGcode также полностью интегрирован с буфером обмена операционной системы через API Async Clipboard. Вы можете вставлять изображения из проводника операционной системы в приложение, нажав кнопку «Вставить изображение» или нажав команду или элемент управления плюс v на клавиатуре.

Вставка изображения из проводника в SVGcode.

API Async Clipboard недавно получил возможность работать и с изображениями SVG, поэтому вы также можете скопировать изображение SVG и вставить его в другое приложение для дальнейшей обработки.

Копирование изображения из SVGcode в 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'));
});

Чтобы узнать больше, прочтите статью об Async Clipboard или просмотрите файл src/js/clipboard.js .

Обработка файлов

Одна из моих любимых особенностей SVGcode — то, насколько хорошо он сочетается с операционной системой. Будучи установленным PWA, он может стать обработчиком файлов или даже обработчиком файлов по умолчанию для файлов изображений. Это означает, что когда я нахожусь в Finder на своем компьютере с MacOS, я могу щелкнуть изображение правой кнопкой мыши и открыть его с помощью SVGcode. Эта функция называется обработкой файлов и работает на основе свойства file_handlers в манифесте веб-приложения и очереди запуска, что позволяет приложению использовать переданный файл.

Открытие файла с рабочего стола с установленным приложением 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;
    }
  }
});

Дополнительные сведения см. в разделе Разрешить установленным веб-приложениям быть обработчиками файлов и просмотрите исходный код в src/js/filehandling.js .

Общий веб-ресурс (файлы)

Еще одним примером слияния с операционной системой является функция общего доступа приложения. Предполагая, что я хочу внести изменения в SVG, созданный с помощью SVGcode, один из способов справиться с этим — сохранить файл, запустить приложение для редактирования SVG, а затем открыть файл SVG оттуда. Однако более плавный процесс — использовать API Web Share , который позволяет напрямую обмениваться файлами. Таким образом, если приложение для редактирования SVG является целью общего доступа, оно может напрямую получить файл без отклонений.

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);
      }
    }
  }
});
Публикация изображения SVG в Gmail.

Целевой веб-ресурс (файлы)

И наоборот, SVGcode также может выступать в качестве цели общего доступа и получать файлы из других приложений. Чтобы это работало, приложению необходимо сообщить операционной системе через API-интерфейс Web Share Target, какие типы данных оно может принимать. Это происходит через специальное поле в манифесте веб-приложения.

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

Маршрут action на самом деле не существует, но обрабатывается исключительно обработчиком fetch сервис-воркера, который затем передает полученные файлы для фактической обработки в приложении.

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);
      })(),
    );
  }
});
Публикация снимка экрана в SVGcode.

Заключение

Хорошо, это был краткий обзор некоторых расширенных функций приложений в SVGcode. Я надеюсь, что это приложение станет важным инструментом для обработки ваших изображений наряду с другими замечательными приложениями, такими как Squoosh или SVGOMG .

SVGcode доступен на svgco.de . Видишь, что я там сделал? Вы можете просмотреть его исходный код на GitHub . Обратите внимание: поскольку Potrace имеет лицензию GPL, то же самое относится и к SVGcode. На этом удачной векторизации! Я надеюсь, что SVGcode будет полезен, а некоторые его функции могут вдохновить вас на создание следующего приложения.

Благодарности

Эта статья была рассмотрена Джо Медли .