Imagens para Web
Neste artigo, vou apresentar algumas dicas para trabalhar com imagens no desenvolvimento web
Formato de imagem
Sempre que possível, utilize SVG. Esse formato é leve e não perde qualidade ao ser redimensionado. Caso não seja possível, prefira formatos como WebP e AVIF em vez de JPG e PNG. Aproximadamente 95% dos navegadores são compatíveis com esses formatos modernos, segundo o site Can I Use
Utilizando o ImageMagick, podemos converter uma imagem PNG para WebP com o comando
convert input.png output.webp
O PNG original tinha 5 MB, e o WebP convertido ficou com 88 kB. No entanto, ao comparar as imagens, é possível perceber que a versão em WebP apresenta qualidade inferior. Isso acontece porque, por padrão, o ImageMagick realiza uma conversão com perdas (lossy), na qual há perda de informação
Por outro lado, o WebP também suporta conversão sem perdas (lossless), em que as informações originais são preservadas
convert input.png -define webp:lossless=true output.webp
Nesse caso, a imagem convertida ficou com 4 MB. Porém, ao converter um JPG de 71 kB para WebP usando o modo lossless, o resultado final foi 228 kB. Portanto, sempre compare os tamanhos dos arquivos antes de decidir qual abordagem utilizar
Minificação de SVG
Também é possível reduzir o espaço ocupado por arquivos SVG. Para isso, podemos utilizar o pacote NPM svgo com o seguinte comando
npx svgo input.svg -o output.svg
O SVG original tinha 44 kB, enquanto a versão minificada ficou com 26 kB.
Lazy Loading
A tag <img> do HTML aceita o atributo loading, que pode ter o valor lazy ou, por padrão, eager. Quando definido como lazy, a imagem só é carregada quando está próxima da área visível da tela. Assim, se a imagem estiver no final da página, ela só será carregada quando o usuário rolar até ela
Nesse exemplo, a primeira imagem será carregada imediatamente, enquanto a segunda será carregada apenas quando necessário
<img src="image-1.jpg" loading="eager" />
<img src="image-2.jpg" loading="lazy" />
Imagens Progressivas
Se você precisar renderizar uma imagem JPG grande, pode convertê-la para o formato progressivo com o seguinte comando
convert input.jpg -interlace Plane output.jpg
Ao converter para progressivo, o tamanho do arquivo pode aumentar levemente (por exemplo, de 6,5 MB para 6,6 MB)
Em uma imagem progressiva, o navegador inicialmente exibe uma versão de baixa qualidade e, conforme mais dados são recebidos, a qualidade é refinada até atingir a versão final
Placeholder de carregamento
Enquanto a imagem ainda não foi totalmente baixada, podemos exibir um estado de carregamento. No exemplo abaixo, utilizo React para implementar esse comportamento
export function Image({ src }) {
const [loaded, setLoaded] = useState(false);
return (
<div
style={{
position: "relative",
height: "216px",
width: "384px",
}}
>
{!loaded && (
<div
style={{
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
backgroundColor: "gray",
}}
/>
)}
<img
src={src}
style={{
width: "100%",
height: "100%",
objectFit: "cover",
opacity: loaded ? 1 : 0,
}}
onLoad={() => setLoaded(true)}
/>
</div>
);
}
Nesse caso, enquanto a imagem não termina de carregar, um bloco cinza é exibido no lugar dela
Layout Shift
Enquanto a imagem ainda não foi carregada, o navegador não sabe quanto espaço ela ocupará na página. Isso pode causar um efeito conhecido como layout shift, no qual elementos da página (como textos) se movem conforme outros conteúdos são carregados
Para evitar esse problema, é importante definir os atributos width e height da imagem, seja diretamente na tag HTML ou via CSS. Assim, o navegador já reserva o espaço correto antes do carregamento da imagem
Preload de imagem
Se a página inicial exibe uma imagem, o fluxo normal costuma ser:
- O navegador baixa o
index.html - O
index.htmlreferencia oindex.js - Após executar o JavaScript, os componentes são renderizados
- Só então a imagem é requisitada e baixada
Como você já sabe que essa imagem será necessária na primeira tela, pode antecipar o carregamento adicionando a seguinte tag no <head> do index.html
<link rel="preload" as="image" href="./src/image.jpg">
Assim, ao acessar o site, o navegador baixa o index.html e, imediatamente, inicia em paralelo o download do index.js e da imagem, pois ambos já estão explicitamente referenciados no HTML
Resolução de imagem
Se você tem uma imagem de 16×16 pixels e precisa exibi-la em um espaço de 8×8 pixels, é possível reorganizar os pixels, mesclando alguns e removendo outros
Quando você possui uma imagem com resolução de 2000×2000, mas define no código um tamanho de 100×100 via HTML ou CSS, o navegador faz essencialmente o mesmo processo durante a renderização: descarta informações visuais para exibi-la no tamanho correto
No entanto, o usuário ainda precisa baixar a imagem original completa, transferindo um arquivo muito maior do que o necessário. Para evitar isso, podemos redimensionar corretamente a imagem:
convert input.webp -resize 100x100 output.webp
Nesse processo, a imagem perde informação. Porém, em cenários como e-commerce, onde o usuário pode aplicar zoom na imagem do produto, pode ser necessário disponibilizar versões com resolução maior
Se você utilizar o Lighthouse para analisar a performance do seu site, servir imagens com resolução inadequada é um dos fatores que impactam negativamente a pontuação
Imagens Responsivas
Seguindo o tópico anterior, imagine que você definiu uma imagem com tamanho 300×300 no código. No desktop, ela parece nítida. Porém, ao abrir no celular, a imagem fica borrada
Isso ocorre porque celulares geralmente possuem um DPR (Device Pixel Ratio) mais alto, normalmente entre 2 e 3, enquanto no desktop geralmente é 1 (podendo variar conforme sistema operacional, zoom e hardware). Você pode verificar o DPR com:
window.devicePixelRatio
Em uma tela com DPR 2, 1 pixel virtual equivale a 4 pixels reais. Quando definimos width e height via HTML ou CSS, estamos trabalhando com pixels virtuais. Porém, na renderização, o navegador precisa preencher pixels reais da tela. Assim, uma imagem de 300×300 pixels virtuais será renderizada como 600×600 pixels reais, o que pode causar perda de qualidade
Para resolver isso, podemos servir imagens com diferentes resoluções, escolhidas automaticamente conforme o DPR do dispositivo:
<img
srcSet="image-384w.jpg, image-768w.jpg 2x, image-960w.jpg 3x"
src="image-384w.jpg"
width="384"
/>
Nesse caso, o navegador selecionará automaticamente a imagem com a resolução mais adequada ao DPR configurado.
Essa abordagem costuma valer a pena para imagens com tamanho fixo ou pequenas, como fotos de usuários. Uma estratégia simples é definir um tamanho base (por exemplo, 384) e gerar versões com larguras 384, 768 (384×2) e 960 (384×3), cobrindo os DPRs mais comuns
Para imagens responsivas ou grandes (como banners), você pode utilizar o atributo sizes, definindo qual resolução deve ser usada com base em breakpoints. Outra alternativa é utilizar a tag <picture> para servir imagens diferentes conforme o contexto. No entanto, isso adiciona maior complexidade e nem sempre compensa, já que a imagem redimensionada para uma tela menor normalmente já apresentará boa qualidade
Para mais detalhes, consulte a documentação do MDN Web Docs
Publicação orignal com imagens de exemplo: Medium