SVGcode: một PWA để chuyển đổi hình ảnh đường quét thành đồ họa vectơ SVG

SVGcode là một Ứng dụng web tiến bộ cho phép bạn chuyển đổi hình ảnh đường quét như JPG, PNG, GIF, WebP, AVIF, v.v. thành đồ hoạ vectơ ở định dạng SVG. Dịch vụ này sử dụng API Truy cập hệ thống tệp, API Bảng nhớ tạm không đồng bộ, API Xử lý tệp và tuỳ chỉnh Lớp phủ chế độ điều khiển cửa sổ.

(Nếu bạn thích xem hơn là đọc, bài viết này cũng có sẵn dưới dạng video.)

Từ đường quét thành vectơ

Bạn đã bao giờ điều chỉnh tỷ lệ hình ảnh nhưng kết quả bị vỡ hình và không đạt yêu cầu chưa? Nếu Do đó, bạn có thể đã xử lý định dạng hình ảnh đường quét như WebP, PNG hoặc JPG.

Việc mở rộng hình ảnh đường quét khiến hình ảnh đó trông có dạng pixel.

Ngược lại, đồ hoạ vectơ là hình ảnh được xác định bởi các điểm trong hệ toạ độ. Các các điểm được nối bằng các đường và đường cong tạo thành đa giác và các hình dạng khác. Đồ hoạ vectơ có tận dụng đồ hoạ đường quét vì chúng có thể được tăng hoặc giảm ở độ phân giải bất kỳ mà không bị vỡ ảnh.

Mở rộng hình ảnh vectơ mà không làm giảm chất lượng.

Giới thiệu về SVGcode

Tôi đã tạo một ứng dụng web tiến bộ (PWA) có tên là SVGcode có thể giúp bạn chuyển đổi hình ảnh đường quét thành vectơ. Tín dụng khi khoản tín dụng đến hạn: Tôi không phát minh ra tính năng này. Với SVGcode, tôi chỉ cần đứng trên của công cụ dòng lệnh có tên Potrace bằng cách Peter Selinger mà tôi có được chuyển đổi thành Web hội, vì vậy, nó có thể được sử dụng trong Ứng dụng web.

Ảnh chụp màn hình ứng dụng SVGcode.
Ứng dụng SVGcode.

Sử dụng mã SVG

Trước tiên, tôi muốn hướng dẫn bạn cách dùng ứng dụng này. Tôi bắt đầu với hình ảnh giới thiệu cho Hội nghị dành cho nhà phát triển Chrome mà tôi đã tải xuống từ kênh Twitter ChromiumDev. Đây là hình ảnh đường quét PNG mà tôi kéo vào ứng dụng SVGcode. Khi tôi thả tệp, ứng dụng sẽ theo dõi màu của hình ảnh theo màu sắc, cho đến khi phiên bản vectơ hoá của đầu vào xuất hiện. Bây giờ, tôi có thể phóng to hình ảnh và như bạn có thể thấy, các cạnh vẫn sắc nét. Nhưng phóng to biểu trưng Chrome, bạn có thể thấy tính năng theo dõi hoàn hảo và đặc biệt là các đường viền của biểu trưng trông hơi lốm đốm. Tôi có thể cải thiện kết quả bằng cách loại bỏ dấu vết bằng cách triệt tiêu các đốm lên đến, giả sử, 5 pixel.

Chuyển đổi hình ảnh bị thả thành SVG.

Áp phích trong SVGcode

Một bước quan trọng cho quá trình vectơ hoá, đặc biệt là đối với ảnh chụp, là áp dụng hiệu ứng đầu vào để giảm số lượng màu. SVGcode cho phép tôi thực hiện việc này theo kênh màu và xem SVG thu được khi tôi thực hiện thay đổi. Khi đã hài lòng với kết quả, tôi có thể lưu SVG vào ổ đĩa cứng của mình và sử dụng ở bất cứ nơi nào tôi muốn.

Giảm số lượng màu cho hình ảnh.

API được dùng trong mã SVG

Giờ bạn đã biết những chức năng của ứng dụng, giờ hãy để tôi cho bạn thấy một số API giúp đảm bảo điều kỳ diệu sẽ xảy ra.

Ứng dụng web tiến bộ

SVGcode là một Ứng dụng web tiến bộ có thể cài đặt, do đó được bật hoàn toàn ngoại tuyến. Ứng dụng này dựa trên trên Mẫu JS Vanilla cho Vite.js và sử dụng các phương pháp ứng dụng web tiến bộ (PWA) trình bổ trợ Vite sẽ tạo một trình chạy dịch vụ sử dụng Workbox.js nâng cao. Workbox là một tập hợp về các thư viện có thể hỗ trợ trình chạy dịch vụ sẵn sàng sản xuất cho Ứng dụng web tiến bộ, Mẫu này có thể không nhất thiết phải hoạt động với tất cả ứng dụng, nhưng đối với trường hợp sử dụng của SVGcode thì thật tuyệt.

Lớp phủ chế độ điều khiển cửa sổ

Để tối đa hoá không gian màn hình có sẵn, SVGcode sử dụng Tuỳ chỉnh Lớp phủ điều khiển cửa sổ bằng cách di chuyển trình đơn chính lên vùng thanh tiêu đề. Bạn có thể thấy tính năng này được kích hoạt vào cuối quy trình cài đặt.

Cài đặt mã SVG và kích hoạt chế độ tuỳ chỉnh Lớp phủ điều khiển cửa sổ.

API Truy cập hệ thống tệp

Để mở tệp hình ảnh đầu vào và lưu các SVG thu được, tôi sử dụng File System Access API (API Truy cập hệ thống tệp). Điều này cho phép tôi giữ lại tham chiếu đến đã mở tệp và để tiếp tục từ nơi tôi đã dừng lại, ngay cả sau khi ứng dụng tải lại. Bất cứ khi nào một hình ảnh nhận được đã lưu, nội dung đó sẽ được tối ưu hoá thông qua thư viện svgo. Quá trình này có thể mất chút thời gian, tuỳ thuộc vào độ phức tạp của SVG. Cần có cử chỉ của người dùng để hiển thị hộp thoại lưu tệp. Đó là do đó, việc lấy dữ liệu xử lý tệp trước khi tối ưu hoá SVG là rất quan trọng, vì vậy người dùng cử chỉ không bị vô hiệu hoá vào thời điểm SVG tối ưu hoá sẵn sàng.

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

Kéo và thả

Để mở hình ảnh nhập, tôi có thể sử dụng tính năng mở tệp hoặc, như bạn đã thấy ở trên, chỉ kéo và thả tệp hình ảnh vào ứng dụng. Tính năng mở tệp khá đơn giản, rất thú vị là trường hợp kéo và thả. Điều đặc biệt thú vị là bạn có thể lấy tên người dùng hệ thống tệp từ mục chuyển dữ liệu qua getAsFileSystemHandle() . Như đã đề cập trước đó, tôi có thể duy trì tên người dùng này để nó sẵn sàng khi ứng dụng được tải lại.

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

Để biết thêm chi tiết, hãy tham khảo bài viết về File System Access API (API Truy cập hệ thống tệp) và nếu bạn quan tâm, hãy nghiên cứu mã nguồn SVGcode trong src/js/filesystem.js.

API Bảng nhớ tạm không đồng bộ

SVGcode cũng được tích hợp đầy đủ với bảng nhớ tạm của hệ điều hành qua API Bảng nhớ tạm không đồng bộ. Bạn có thể dán hình ảnh từ trình khám phá tệp của hệ điều hành vào ứng dụng bằng cách nhấp vào nút dán hình ảnh hoặc nhấn phím Command hoặc phím Control + v trên bàn phím.

Dán một hình ảnh trong trình khám phá tệp vào mã SVGcode.

API Bảng nhớ tạm không đồng bộ gần đây cũng có khả năng xử lý hình ảnh SVG để bạn có thể sao chép cả hình ảnh SVG rồi dán vào một ứng dụng khác để xử lý thêm.

Sao chép hình ảnh từ SVGcode vào 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'));
});

Để tìm hiểu thêm, hãy đọc bài viết Bảng nhớ tạm không đồng bộ hoặc xem tệp src/js/clipboard.js.

Xử lý tệp

Một trong những tính năng mà tôi yêu thích về SVGcode là khả năng kết hợp tốt với hệ điều hành. Là một đã cài đặt PWA, thì ứng dụng này có thể trở thành một trình xử lý tệp hoặc thậm chí là trình xử lý tệp mặc định đối với tệp hình ảnh. Chiến dịch này có nghĩa là khi tôi đang mở Finder trên máy macOS của mình, tôi có thể nhấp chuột phải vào một hình ảnh và mở nó bằng Mã SVG (Đồ hoạ vectơ có thể mở rộng). Tính năng này được gọi là Xử lý tệp và hoạt động dựa trên thuộc tính file_handlers trong Tệp kê khai ứng dụng web và hàng đợi khởi chạy, cho phép ứng dụng sử dụng tệp đã truyền.

Mở một tệp trên màn hình bằng ứng dụng SVGcode đã cài đặt.
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;
    }
  }
});

Để biết thêm thông tin, hãy xem phần Cho phép các ứng dụng web đã cài đặt là trình xử lý tệp và xem mã nguồn trong src/js/filehandling.js.

Chia sẻ trên web (tệp)

Một ví dụ khác về việc kết hợp với hệ điều hành là tính năng chia sẻ của ứng dụng. Giả sử tôi muốn để thực hiện chỉnh sửa đối với SVG được tạo bằng SVGcode, một cách để xử lý vấn đề này là lưu tệp, khởi chạy ứng dụng chỉnh sửa SVG, rồi mở tệp SVG từ đó. Tuy nhiên, suôn sẻ hơn là sử dụng API Chia sẻ web để cho phép chia sẻ trực tiếp tệp. Vậy nếu ứng dụng chỉnh sửa SVG là mục tiêu chia sẻ, ứng dụng này có thể trực tiếp nhận tệp mà không có độ lệch.

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);
      }
    }
  }
});
Chia sẻ hình ảnh SVG với Gmail.

Mục tiêu chia sẻ web (Tệp)

Ngược lại, SVGcode cũng có thể đóng vai trò là mục tiêu chia sẻ và nhận tệp từ các ứng dụng khác. Người nhận để quy trình này hoạt động, ứng dụng cần cho hệ điều hành biết thông qua Web Share Target API có thể chấp nhận loại dữ liệu. Quá trình này diễn ra thông qua một trường riêng trong Tệp kê khai ứng dụng 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"]
        }
      ]
    }
  }
}

Tuyến action không thực sự tồn tại mà chỉ được xử lý trong fetch của trình chạy dịch vụ trình xử lý này, sau đó chuyển các tệp đã nhận để xử lý thực tế trong ứng dụng.

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);
      })(),
    );
  }
});
Chia sẻ ảnh chụp màn hình với SVGcode.

Kết luận

Được rồi. Sau đây là hướng dẫn nhanh về một số tính năng nâng cao của ứng dụng trong SVGcode. Tôi hy vọng ứng dụng này có thể trở thành một công cụ thiết yếu cho nhu cầu xử lý hình ảnh của bạn cùng với những ứng dụng thú vị khác như Squoosh hoặc SVGOMG.

Mã SVG hiện có tại svgco.de. Bạn có muốn xem những gì mình đã làm ở đó không? Bạn có thể xem lại mã nguồn của trang trên GitHub. Xin lưu ý rằng vì Potrace là Được GPL cấp phép, nên mã SVGcode cũng vậy. Chúc mừng bạn đã tạo vectơ! Tôi hy vọng SVGcode sẽ hữu ích, và một số tính năng có thể truyền cảm hứng cho ứng dụng mới của bạn.

Xác nhận

Bài viết này do Joe Medley xem xét.