O SVGcode é um Progressive Web App que permite converter imagens rasterizadas, como JPG, PNG, GIF, WebP, AVIF etc., em gráficos vetoriais no formato SVG. Ele usa a API File System Access, a API Async Clipboard, a API File Handling e a personalização da Window Controls Overlay.
De raster para vetor
Você já dimensionou uma imagem e o resultado ficou pixelado e insatisfatório? Se for o caso, você provavelmente lidou com um formato de imagem raster, como WebP, PNG ou JPG.
Em contraste, os gráficos vetoriais são imagens definidas por pontos em um sistema de coordenadas. Esses pontos são conectados por linhas e curvas para formar polígonos e outras formas. Os gráficos vetoriais têm uma vantagem em relação aos gráficos raster, já que podem ser dimensionados para cima ou para baixo em qualquer resolução sem pixelização.
Introdução ao SVGcode
Eu criei um PWA chamado SVGcode que pode ajudar você a converter imagens raster em vetores. Crédito onde é devido: eu não inventei isso. Com o SVGcode, eu apenas uso uma ferramenta de linha de comando chamada Potrace, de Peter Selinger, que converti para o Web Assembly, para que possa ser usada em um app da Web.
Usando o SVGcode
Primeiro, quero mostrar como usar o app. Vou começar com a imagem de apresentação do Chrome Dev Summit que baixei do canal do ChromiumDev no Twitter. Esta é uma imagem raster PNG que arrasto para o app SVGcode. Quando deixo o arquivo, o app rastreia a imagem cor por cor, até que uma versão vetorizada da entrada apareça. Agora posso dar zoom na imagem, e, como você pode ver, as bordas permanecem nítidas. Mas, ao ampliar o logotipo do Chrome, você pode notar que o rastreamento não foi perfeito, e os contornos do logotipo parecem um pouco pontilhados. Posso melhorar o resultado removendo as manchas do rastreamento, suprimindo manchas de até cinco pixels.
Posterização no código SVG
Uma etapa importante para a vetorização, especialmente para imagens fotográficas, é posterizar a imagem de entrada para reduzir o número de cores. O SVGcode permite fazer isso por canal de cor e conferir o SVG resultante conforme faço as mudanças. Quando estou satisfeito com o resultado, posso salvar o SVG no meu disco rígido e usá-lo onde quiser.
APIs usadas no SVGcode
Agora que você já sabe o que o app pode fazer, vamos mostrar algumas das APIs que ajudam a criar a magia.
App Web Progressivo
O SVGcode é um Progressive Web App instalável e, portanto, totalmente compatível com o modo off-line. O app é baseado no modelo Vanilla JS para Vite.js e usa o popular plug-in do Vite PWA, que cria um worker de serviço que usa Workbox.js. O Workbox é um conjunto de bibliotecas que podem acionar um service worker pronto para produção em Progressive Web Apps. Esse padrão não funciona necessariamente para todos os apps, mas é ótimo para o caso de uso do SVGcode.
Sobreposição dos controles da janela
Para maximizar o espaço disponível na tela, o SVGcode usa a personalização da Window Controls Overlay movendo o menu principal para cima na área da barra de título. Você pode conferir a ativação no final do fluxo de instalação.
API File System Access
Para abrir arquivos de imagem de entrada e salvar os SVGs resultantes, uso a API File System Access. Isso permite que eu mantenha uma referência aos arquivos abertos anteriormente e continue de onde parei, mesmo após uma recarga do app. Sempre que uma imagem é salva, ela é otimizada pela biblioteca svgo, o que pode levar algum tempo, dependendo da complexidade do SVG. A exibição da caixa de diálogo de salvamento de arquivo requer um gesto do usuário. Portanto, é importante conseguir o identificador de arquivo antes da otimização do SVG, para que o gesto do usuário não seja invalidado quando o SVG otimizado estiver pronto.
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);
}
Arrastar e soltar
Para abrir uma imagem de entrada, posso usar o recurso de abertura de arquivo ou, como você viu acima,
arrastar e soltar um arquivo de imagem no app. O recurso de abertura de arquivo é bastante simples, mas
o caso de arrastar e soltar é mais interessante. O bom disso é que você pode
receber um identificador de sistema de arquivos do item de transferência de dados pelo
método
getAsFileSystemHandle()
. Como mencionado anteriormente, posso manter esse identificador para que ele esteja pronto quando o app for recarregado.
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);
}
});
Para mais detalhes, consulte o artigo sobre a API File System Access e,
se você tiver interesse, estude o código-fonte do SVGcode em
src/js/filesystem.js
.
API Async Clipboard
O SVGcode também é totalmente integrado à área de transferência do sistema operacional pela API Async Clipboard. É possível colar imagens do explorador de arquivos do sistema operacional no app clicando no botão "Colar imagem" ou pressionando Command ou Control e V no teclado.
Recentemente, a API Async Clipboard ganhou a capacidade de lidar com imagens SVG. Assim, você também pode copiar uma imagem SVG e colá-la em outro aplicativo para processamento adicional.
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'));
});
Para saber mais, leia o artigo Área de transferência assíncrona ou consulte o arquivo
src/js/clipboard.js
.
Processamento de arquivos
Um dos meus recursos favoritos do SVGcode é como ele se integra ao sistema operacional. Como um PWA instalado, ele pode se tornar um gerenciador de arquivos ou até mesmo o gerenciador de arquivos padrão para arquivos de imagem. Isso significa que, quando estou no Finder na minha máquina macOS, posso clicar com o botão direito do mouse em uma imagem e abri-la com SVGcode. Esse recurso é chamado de Processamento de arquivos e funciona com base na propriedade file_handlers no manifesto do app da Web e na fila de inicialização, o que permite que o app consuma o arquivo transmitido.
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;
}
}
});
Para mais informações, consulte Permitir que aplicativos da Web instalados sejam gerenciadores de arquivos e confira o código-fonte em
src/js/filehandling.js
.
Compartilhamento na Web (arquivos)
Outro exemplo de integração com o sistema operacional é o recurso de compartilhamento do app. Supondo que eu queira fazer edições em um SVG criado com o SVGcode, uma maneira de lidar com isso seria salvar o arquivo, iniciar o app de edição de SVG e abrir o arquivo SVG a partir dele. Um fluxo mais suave, no entanto, é usar a API Web Share, que permite que os arquivos sejam compartilhados diretamente. Portanto, se o app de edição de SVG for um destino de compartilhamento, ele poderá receber o arquivo diretamente sem desvio.
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);
}
}
}
});
Destino de compartilhamento da Web (arquivos)
Por outro lado, o SVGcode também pode atuar como um destino de compartilhamento e receber arquivos de outros apps. Para fazer isso, o app precisa informar ao sistema operacional, pela API de destino de compartilhamento da Web, quais tipos de dados ele pode aceitar. Isso acontece em um campo dedicado no manifesto do app da 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"]
}
]
}
}
}
A rota action
não existe, mas é processada apenas no gerenciador fetch
do worker do serviço, que transmite os arquivos recebidos para processamento real no app.
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);
})(),
);
}
});
Conclusão
Esse foi um tour rápido pelos recursos avançados do app no SVGcode. Espero que esse app se torne uma ferramenta essencial para suas necessidades de processamento de imagens, junto com outros apps incríveis, como Squoosh ou SVGOMG.
O SVGcode está disponível em svgco.de. Viu o que eu fiz? Confira o código-fonte no GitHub. Como o Potrace tem licença GPL, o SVGcode também tem. Agora é só vetorizar! Espero que o SVGcode seja útil e que alguns dos recursos dele possam inspirar seu próximo app.
Agradecimentos
Este artigo foi revisado por Joe Medley.