SVGcode ist eine progressive Web-App, mit der Sie Rasterbilder wie JPG, PNG, GIF, WebP, AVIF usw. in Vektorgrafiken im SVG-Format umwandeln können. Dabei werden die File System Access API, die Async Clipboard API, die File Handling API und die Anpassung des Window Controls Overlays verwendet.
Von Raster zu Vektor
Haben Sie schon einmal ein Bild skaliert und das Ergebnis war verpixelt und nicht zufriedenstellend? In diesem Fall kennen Sie sich wahrscheinlich mit einem Rasterbildformat wie WebP, PNG oder JPG aus.
Vektorgrafiken sind hingegen Bilder, die durch Punkte in einem Koordinatensystem definiert werden. Diese Punkte sind durch Linien und Kurven zu Polygonen und anderen Formen verbunden. Vektorgrafiken haben gegenüber Rastergrafiken den Vorteil, dass sie auf eine beliebige Auflösung ohne Verpixelung skaliert werden können.
Jetzt neu: SVGcode
Ich habe eine PWA namens SVGcode erstellt, mit der Sie Rasterbilder in Vektoren umwandeln können. Anerkennung für die Mitwirkenden: Ich habe das nicht erfunden. Mit SVGcode stehe ich einfach auf den Schultern eines Befehlszeilentools namens Potrace von Peter Selinger, das ich in Web Assembly konvertiert habe, damit es in einer Webanwendung verwendet werden kann.
![Screenshot der SVGcode-Anwendung](https://web.dev/static/case-studies/svgcode/image/svgcode-application-scree-7ad5734e2e5e1.png?authuser=6&hl=de)
SVG-Code verwenden
Zuerst möchte ich dir zeigen, wie die App funktioniert. Ich beginne mit dem Teaser-Image vom Chrome Dev Summit, den ich vom ChromiumDev Twitter-Kanal heruntergeladen habe. Dies ist ein PNG-Rasterbild, das ich dann in die SVGcode-App ziehe. Wenn ich die Datei ablege, verfolgt die App die Bildfarbe nach Farbe, bis eine vektorisierte Version der Eingabe angezeigt wird. Jetzt kann ich das Bild heranzoomen. Die Ränder bleiben scharf. Beim Heranzoomen des Chrome-Logos stellen Sie jedoch fest, dass das Tracing nicht perfekt war und vor allem die Umrisse des Logos ein wenig gesprengt sind. Das Ergebnis kann ich verbessern, indem ich Speppen von bis zu fünf Pixeln unterdrücke.
Posterisierung in SVGcode
Ein wichtiger Schritt bei der Vektorisierung, insbesondere bei fotografischen Bildern, besteht darin, das Eingabebild zu posten, um die Anzahl der Farben zu reduzieren. Mit SVGcode kann ich dies für jeden Farbkanal tun und das resultierende SVG sehen, wenn ich Änderungen vornehme. Wenn ich mit dem Ergebnis zufrieden bin, kann ich die SVG-Datei auf meiner Festplatte speichern und überall dort verwenden, wo ich möchte.
In SVGcode verwendete APIs
Nachdem Sie nun gesehen haben, was die App kann, möchte ich Ihnen einige der APIs zeigen, mit denen das möglich ist.
Progressive Web-App
SVGcode ist eine installierbare progressive Web-App, die vollständig offline aktiviert werden kann. Die App basiert auf der Vanilla JS-Vorlage für Vite.js und verwendet das beliebte Vite-Plug-in PWA, das einen Service Worker erstellt, der Workbox.js im Hintergrund verwendet. Workbox besteht aus einer Reihe von Bibliotheken, die einen produktionsreifen Service Worker für progressive Web-Apps unterstützen können. Dieses Muster funktioniert möglicherweise nicht unbedingt für alle Apps, für SVGcode-Anwendungsfall ist es jedoch hervorragend.
Overlay für Fenstersteuerelemente
Um den verfügbaren Platz auf dem Bildschirm zu maximieren, verwendet SVGcode die Anpassung des Overlays für Fenstersteuerelemente, indem das Hauptmenü nach oben in den Bereich der Titelleiste verschoben wird. Die Aktivierung wird am Ende der Installation angezeigt.
File System Access API
Um die eingegebenen Bilddateien zu öffnen und die resultierenden SVGs zu speichern, verwende ich die File System Access API. So kann ich auf zuvor geöffnete Dateien verweisen und dort weitermachen, wo ich aufgehört habe, auch nach einem Neuladevorgang der App. Immer, wenn ein Bild gespeichert wird, wird es mithilfe der svgo-Bibliothek optimiert. Dies kann je nach Komplexität des SVG einen Moment dauern. Zum Anzeigen des Dialogfelds zum Speichern der Datei ist eine Nutzergeste erforderlich. Daher ist es wichtig, das Datei-Handle vor der SVG-Optimierung abzurufen, damit die Touch-Geste für den Nutzer nicht ungültig wird, wenn das optimierte SVG bereit ist.
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);
}
Drag-and-Drop
Zum Öffnen eines Eingabebildes kann ich entweder die Funktion zum Öffnen von Dateien verwenden oder, wie Sie oben gesehen haben, einfach eine Bilddatei per Drag-and-drop in die App ziehen. Die Funktion zum Öffnen von Dateien ist ziemlich geradlinig, interessanter als Drag-and-drop. Das Besondere daran ist, dass Sie mit der Methode getAsFileSystemHandle()
ein Dateisystem-Handle aus dem Datenübertragungselement abrufen können. Wie bereits erwähnt, kann ich diesen Alias beibehalten, damit er bereit ist, wenn die App neu geladen wird.
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);
}
});
Weitere Informationen finden Sie im Artikel zur File System Access API. Außerdem können Sie sich bei Interesse den SVG-Quellcode in src/js/filesystem.js
ansehen.
Async Clipboard API
SVGcode ist über die Async Clipboard API außerdem vollständig in die Zwischenablage des Betriebssystems integriert. Sie können Bilder aus dem Datei-Explorer des Betriebssystems in die Anwendung einfügen. Klicken Sie dazu entweder auf die Schaltfläche „Bild einfügen“ oder drücken Sie die Befehlstaste oder Strg + V auf Ihrer Tastatur.
Mit der Async Clipboard API können seit Kurzem auch SVG-Bilder verarbeitet werden. Sie können also ein SVG-Bild auch kopieren und zur weiteren Verarbeitung in eine andere Anwendung einfügen.
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'));
});
Weitere Informationen finden Sie im Artikel Asynchrone Zwischenablage oder in der Datei src/js/clipboard.js
.
Dateihandhabung
Eine meiner Lieblingsfunktionen von SVGcode ist die Einbindung in das Betriebssystem. Als installierte PWA kann sie ein Datei-Handler oder sogar der Standard-Datei-Handler für Bilddateien werden. Das bedeutet, dass ich im Finder auf meinem macOS-Computer mit der rechten Maustaste auf ein Bild klicken und es mit SVGcode öffnen kann. Diese Funktion wird als Dateiverarbeitung bezeichnet und basiert auf der Eigenschaft „file_handlers“ im Web-App-Manifest und auf der Startwarteschlange, mit der die Anwendung die übergebene Datei verarbeiten kann.
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;
}
}
});
Weitere Informationen finden Sie unter Installierte Webanwendungen als Datei-Handler verwenden. Den Quellcode finden Sie in src/js/filehandling.js
.
Web Share (Dateien)
Ein weiteres Beispiel für die Integration in das Betriebssystem ist die Freigabefunktion der App. Angenommen, ich möchte eine mit SVGcode erstellte SVG bearbeiten. Eine Möglichkeit dafür wäre, die Datei zu speichern, die SVG-Bearbeitungs-App zu starten und die SVG-Datei von dort zu öffnen. Ein reibungsloserer Ablauf ist jedoch die Verwendung der Web Share API, mit der Dateien direkt freigegeben werden können. Wenn also die SVG-Bearbeitungs-App ein Ziel zum Teilen ist, kann sie die Datei direkt und ohne Abweichung empfangen.
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);
}
}
}
});
Web Share Target (Dateien)
Umgekehrt kann SVG-Code auch zum Teilen von Inhalten genutzt werden und Dateien von anderen Apps empfangen. Dazu muss die App dem Betriebssystem über die Web Share Target API mitteilen, welche Datentypen sie akzeptieren kann. Dies geschieht über ein spezielles Feld im Web App Manifest.
{
"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"]
}
]
}
}
}
Die Route action
existiert nicht, sondern wird ausschließlich im fetch
-Handler des Service Workers verarbeitet, der dann empfangene Dateien zur tatsächlichen Verarbeitung in der Anwendung übergibt.
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);
})(),
);
}
});
Fazit
Alles klar, das war eine kurze Tour durch einige der erweiterten App-Funktionen in SVGcode. Ich hoffe, dass diese App neben anderen tollen Apps wie Squoosh oder SVGOMG zu einem unverzichtbaren Tool für Ihre Bildverarbeitung werden kann.
SVGcode ist unter svgco.de verfügbar. Sehen Sie, was ich dort gemacht habe? Sie können den Quellcode auf GitHub prüfen. Da Potrace GPL-lizenziert ist, gilt dies auch für SVGcode. Viel Spaß beim Vektoren! Ich hoffe, SVGcode ist nützlich und Sie inspirieren Ihre nächste App.
Danksagungen
Dieser Artikel wurde von Joe Medley geprüft.