SVGcode est une application Web progressive qui vous permet de convertir des images matricielles telles que JPG, PNG, GIF, WebP, AVIF, etc. en graphiques vectoriels au format SVG. Elle utilise l'API File System Access, l'API Async Clipboard, l'API File Handling et la personnalisation de l'interface Window Controls Overlay.
Passer du raster au vecteur
Avez-vous déjà redimensionné une image et obtenu un résultat pixellisé et insatisfaisant ? Si c'est le cas, vous avez probablement déjà travaillé avec un format d'image matricielle tel que WebP, PNG ou JPG.
À l'inverse, les graphiques vectoriels sont des images définies par des points dans un système de coordonnées. Ces points sont reliés par des lignes et des courbes pour former des polygones et d'autres formes. Les graphiques vectoriels ont un avantage sur les graphiques matriciels en ce sens qu'ils peuvent être mis à l'échelle de n'importe quelle résolution sans pixellisation.
Présentation de SVGcode
J'ai créé une PWA appelée SVGcode qui peut vous aider à convertir des images matricielles en vecteurs. Je ne suis pas le premier à dire que le crédit doit être attribué à qui de droit. Avec SVGcode, je me base simplement sur un outil de ligne de commande appelé Potrace par Peter Selinger que j'ai converti en Web Assembly afin qu'il puisse être utilisé dans une application Web.
Utiliser SVGcode
Tout d'abord, je vais vous montrer comment utiliser l'application. Je commence par l'image teaser du Chrome Dev Summit que j'ai téléchargée sur la chaîne Twitter ChromiumDev. Il s'agit d'une image matricielle PNG que je fais glisser sur l'application SVGcode. Lorsque je dépose le fichier, l'application trace l'image par couleur, jusqu'à ce qu'une version vectorisée de l'entrée s'affiche. Je peux maintenant faire un zoom avant sur l'image. Comme vous pouvez le constater, les bords restent nets. Toutefois, en zoomant sur le logo Chrome, vous pouvez constater que le traçage n'était pas parfait, en particulier que les contours du logo semblent un peu tachés. Je peux améliorer le résultat en supprimant les points du tracé en supprimant les points de jusqu'à cinq pixels, par exemple.
Posterisation dans SVGcode
Une étape importante de la vectorisation, en particulier pour les images photographiques, consiste à posteriser l'image d'entrée pour réduire le nombre de couleurs. SVGcode me permet de le faire par canal de couleur et de voir le SVG résultant à mesure que j'apporte des modifications. Lorsque je suis satisfait du résultat, je peux enregistrer le fichier SVG sur mon disque dur et l'utiliser où je le souhaite.
API utilisées dans SVGcode
Maintenant que vous avez vu ce que l'application est capable de faire, laissez-moi vous présenter certaines des API qui contribuent à faire de la magie.
Progressive Web App
SVGcode est une progressive web app installable et donc entièrement compatible avec le mode hors connexion. L'application est basée sur le modèle Vanilla JS pour Vite.js et utilise le populaire plug-in PWA Vite, qui crée un service worker qui utilise Workbox.js en arrière-plan. Workbox est un ensemble de bibliothèques pouvant alimenter un service worker prêt pour la production pour les applications Web progressives. Ce modèle ne fonctionne pas nécessairement pour toutes les applications, mais il est idéal pour le cas d'utilisation de SVGcode.
Superposition des commandes de fenêtre
Pour maximiser l'espace disponible à l'écran, SVGcode utilise la personnalisation de la superposition des commandes de fenêtre en déplaçant son menu principal vers le haut dans la zone de la barre de titre. Vous pouvez voir cette activation à la fin du processus d'installation.
API File System Access
Pour ouvrir les fichiers d'image d'entrée et enregistrer les SVG qui en résultent, j'utilise l'API File System Access. Cela me permet de conserver une référence aux fichiers précédemment ouverts et de reprendre là où je m'étais arrêté, même après un rechargement de l'application. Chaque fois qu'une image est enregistrée, elle est optimisée via la bibliothèque svgo, ce qui peut prendre un certain temps, en fonction de la complexité du SVG. L'affichage de la boîte de dialogue d'enregistrement de fichier nécessite un geste de l'utilisateur. Il est donc important d'obtenir le gestionnaire de fichiers avant l'optimisation du SVG, afin que le geste de l'utilisateur ne soit pas invalidé au moment où le SVG optimisé est prêt.
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);
}
Glisser-déposer
Pour ouvrir une image d'entrée, je peux utiliser la fonctionnalité d'ouverture de fichier ou, comme vous l'avez vu ci-dessus, simplement glisser-déposer un fichier image sur l'application. La fonctionnalité d'ouverture de fichier est assez simple, mais le cas de glisser-déposer est plus intéressant. L'avantage est que vous pouvez obtenir un gestionnaire de système de fichiers à partir de l'élément de transfert de données via la méthode getAsFileSystemHandle()
. Comme indiqué précédemment, je peux conserver ce gestionnaire afin qu'il soit prêt lorsque l'application est réactivée.
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);
}
});
Pour en savoir plus, consultez l'article sur l'API File System Access et, si vous le souhaitez, étudiez le code source SVGcode dans src/js/filesystem.js
.
API Async Clipboard
SVGcode est également entièrement intégré au presse-papiers du système d'exploitation via l'API Async Clipboard. Vous pouvez coller des images depuis l'explorateur de fichiers du système d'exploitation dans l'application en cliquant sur le bouton "Coller une image" ou en appuyant sur la touche Commande ou Contrôle + V sur votre clavier.
L'API Async Clipboard a récemment acquis la capacité de gérer les images SVG. Vous pouvez donc également copier une image SVG et la coller dans une autre application pour un traitement ultérieur.
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'));
});
Pour en savoir plus, consultez l'article Async Clipboard (Copier-coller asynchrone) ou le fichier src/js/clipboard.js
.
Gestion des fichiers
L'une de mes fonctionnalités préférées du code SVG est la façon dont il s'intègre au système d'exploitation. En tant que PWA installée, elle peut devenir un gestionnaire de fichiers, voire le gestionnaire de fichiers par défaut, pour les fichiers image. Cela signifie que lorsque je suis dans le Finder sur mon Mac, je peux faire un clic droit sur une image et l'ouvrir avec SVGcode. Cette fonctionnalité s'appelle "File Handling" (Gestion des fichiers) et fonctionne en fonction de la propriété file_handlers dans le fichier manifeste de l'application Web et de la file d'attente de lancement, ce qui permet à l'application de consommer le fichier transmis.
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;
}
}
});
Pour en savoir plus, consultez Autoriser les applications Web installées à être des gestionnaires de fichiers et affichez le code source dans src/js/filehandling.js
.
Partage Web (fichiers)
La fonctionnalité de partage de l'application est un autre exemple d'intégration au système d'exploitation. Supposons que je souhaite modifier un SVG créé avec SVGcode. Une solution consiste à enregistrer le fichier, à lancer l'application de modification de SVG, puis à ouvrir le fichier SVG à partir de là. Pour un flux plus fluide, nous vous recommandons d'utiliser l'API Web Share, qui permet de partager des fichiers directement. Par conséquent, si l'application de modification SVG est une cible de partage, elle peut recevoir directement le fichier sans déviation.
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);
}
}
}
});
Cible de partage Web (fichiers)
À l'inverse, le code SVG peut également servir de cible de partage et recevoir des fichiers d'autres applications. Pour que cela fonctionne, l'application doit indiquer au système d'exploitation via l'API Web Share Target les types de données qu'elle peut accepter. Cela se fait via un champ dédié dans le fichier manifeste de l'application 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"]
}
]
}
}
}
Le chemin action
n'existe pas réellement, mais est géré uniquement dans le gestionnaire fetch
du service worker, qui transmet ensuite les fichiers reçus pour un traitement réel dans l'application.
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);
})(),
);
}
});
Conclusion
Nous venons de faire un bref tour d'horizon des fonctionnalités avancées des applications dans SVGcode. J'espère que cette application pourra devenir un outil essentiel pour vos besoins de traitement d'images, aux côtés d'autres applications incroyables comme Squoosh ou SVGOMG.
SVGcode est disponible sur svgco.de. Vous voyez ce que j'ai fait ? Vous pouvez consulter son code source sur GitHub. Notez que, comme Potrace est sous licence GPL, SVGcode l'est également. Je vous souhaite une bonne vectorisation ! J'espère que SVGcode vous sera utile et que certaines de ses fonctionnalités pourront inspirer votre prochaine application.
Remerciements
Cet article a été relu par Joe Medley.