SVGcode 是漸進式網頁應用程式,可將光柵圖片 (例如 JPG、PNG、GIF、WebP、AVIF 等) 轉換為 SVG 格式的向量圖形。這個應用程式會使用 File System Access API、Async Clipboard API、File Handling API 和 Window Controls Overlay 自訂功能。
從光柵轉換為向量
您是否曾調整圖片,但結果有像素化但不滿意?這表示您可能處理的是光柵圖形格式,例如 WebP、PNG 或 JPG。
相反地,向量圖形是透過座標系統中的點定義的圖片。這些點會透過線條和曲線連接,形成多邊形和其他形狀。向量圖形相較於點陣圖,優點在於可縮放至任何解析度,且不會產生像素化現象。
隆重推出 SVGcode
我已建構名為 SVGcode 的 PWA,可協助您將點陣圖轉換為向量圖。把功勞歸給應得的項目:這不是我發明的概念。在 SVGcode 中,我只是站在 Peter Selinger 所開發的 Potrace 指令列工具肩上,並將其轉換為 Web 組合語,以便在網路應用程式中使用。
使用 SVGcode
首先,我想示範應用程式的使用方法 首先是從 Chromium Dev Twitter 頻道下載的 Chrome 開發人員高峰會預告圖片。這是一張 PNG 光柵圖片,我將它拖曳到 SVGcode 應用程式上。當我放置檔案時,應用程式會逐一追蹤圖片的顏色,直到輸入內容的向量版本顯示為止。我現在可以放大圖片,您可以看到邊緣仍保持清晰。不過,放大檢視 Chrome 標誌,您會發現追蹤結果並不完美,尤其是標誌的輪廓看起來有點斑駁。我可以藉由隱藏最多五個像素 (例如 5 個像素) 的凹槽,消除追蹤效果,藉此改善追蹤結果。
在 SVG 程式碼中使用海報化效果
向量化 (尤其是相片圖像) 的重要步驟,就是將輸入圖片轉為海報風格,以減少顏色數量。SVGcode 可讓我針對每個色彩管道執行這項操作,並在變更時查看產生的 SVG。當我滿意結果時,可以將 SVG 儲存到硬碟,並在任何地方使用。
SVGcode 中使用的 API
您已經瞭解應用程式的功能,接下來我將介紹一些 API,協助您發揮應用程式的魔力。
漸進式網頁應用程式
SVGcode 是可安裝的漸進式網頁應用程式,因此可完全離線使用。這個應用程式以 Vanilla JS 範本為基礎,適用於 Vite.js,並使用熱門的 Vite 外掛程式 PWA,可建立服務工作站,在幕後使用 Workbox.js。Workbox 是一組程式庫,可為漸進式網頁應用程式提供可正式發布的服務工作管理員。這個模式不一定適用於所有應用程式,但對於 SVGcode 的用途來說,它非常實用。
視窗控制項重疊
為盡量擴大可用的螢幕空間,SVGcode 使用 Window Controls Overlay 自訂功能,將主選單移至標題列區域。在安裝流程結束時,您可以看到系統已啟用這個功能。
File System Access API
為了開啟輸入圖片檔案並儲存產生的 SVG,我使用 File System Access 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);
}
});
如需詳細資訊,請參閱 File System Access API 的相關文章。如有需要,請在 src/js/filesystem.js
中研究 SVG 程式碼的原始碼。
Async Clipboard API
SVGcode 也透過 Async Clipboard API 與作業系統的剪貼簿全面整合。只要按一下貼上圖片按鈕,或按下鍵盤上的 Command 或 Control + v 鍵,即可將圖片從作業系統的檔案總管貼到應用程式。
Async 剪貼簿 API 最近也能處理可擴充向量圖形圖片,因此您也可以複製可擴充向量圖形圖片,並將其貼到其他應用程式中進行後續處理。
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'));
});
如需更多資訊,請參閱「非同步剪貼簿」一文,或查看檔案 src/js/clipboard.js
。
檔案處理
我最喜歡 SVGcode 的功能之一,就是它能完美融入作業系統。由於這是已安裝的 PWA,因此可以成為圖片檔案的檔案處理常式,甚至是預設檔案處理常式。也就是說,當我在 macOS 電腦的 Finder 中,可以按一下圖片的滑鼠右鍵,然後使用 SVGcode 開啟圖片。這項功能稱為「檔案處理」,會根據網頁應用程式資訊清單和啟動佇列中的 file_handlers 屬性運作,讓應用程式使用傳入的檔案。
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 程式碼建立的 SVG,為解決這個問題,其中一種方法是儲存檔案,啟動 SVG 編輯應用程式,然後從該處開啟 SVG 檔案。不過,使用 Web Share API 可提供更順暢的流程,讓您直接分享檔案。因此,如果 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);
}
}
}
});
網路分享目標 (檔案)
反之,SVGcode 也可以做為分享目標,接收其他應用程式傳送的檔案。如要順利運作,應用程式必須透過 Web Share Target API 告知作業系統可以接受哪些類型的資料。這項作業是透過網頁應用程式資訊清單的專用欄位執行。
{
"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 中的進階應用程式功能。我希望這個應用程式能成為您處理圖片時必備的工具,與 Squoosh 或 SVGOMG 等其他出色的應用程式並駕齊驅。
您可以在 svgco.de 取得 SVGcode。你看我做了什麼?您可以在 GitHub 上查看原始碼。請注意,由於 Potrace 是授權 GPL,因此採用 SVGcode 格式。完成這項任務後,祝您向量地圖一切順利!希望 SVG 程式碼能派上用場 其中的部分功能可激發下個應用程式的靈感
特別銘謝
本文經過 Joe Medley。