Imagens Responsivas

A melhor imagem para as características de tela do dispositivo.

Você está servindo imagens adequadas aos dispositivos de seus usuários?

Hoje dispositivos com os mais diversos tamanhos e resoluções de telas acessam a internet.

Naturalmente, queremos que nosso site apresente-se da melhor maneira possível nessa grande variedade de telas. E as imagens são parte importante para o atingimento desse objetivo.

Ao desenvolver nossas páginas, procuramos usar o mínimo possível de medidas fixas (como o pixel) justamente porque estamos lidando com todo tipo de tela, com tamanhos e resoluções diversas. Portanto, usa-se medidas relativas, como a porcentagem ou o em.

Todavia, se tratando de imagens, não tem muito como escapar. Visto que elas são pixels.

É claro que temos a opção de utilizar Gráficos Vetoriais Escalonáveis, que são os Scalable Vector Graphics SVGs e Icon Fonts. Aliás, use-os sempre que puder. Eles têm um tamanho pequeno e se adaptam bem, estão prontos para serem utilizados em todo tipo de tela, já que podem ser dimensionados para o tamanho que você quiser, sem perder definição. São ótimos para gráficos simples, padrões, elementos de interface, etc.

Mas começa a ficar complexo criar uma imagem baseada em vetor com o tipo de detalhe que você encontraria em uma foto por exemplo. Formatos de imagens rasterizadas, como JPEGs, são melhores para isso.

Quanto maior o tamanho ou qualidade de uma imagem rasterizada, mais pesado tende a ficar seu arquivo.

Carregar uma imagem muito grande em cenários onde o acesso está sendo feito em uma tela pequena ou uma imagem HD em uma tela de resolução baixa, são dois exemplos de desperdício de largura de banda.

Usuários mobile – especialmente – não querem ter que gastar seu pacote de dados baixando uma imagem grande feita para Desktop, quando uma imagem pequena poderia ser feita para seu dispositivo. É por isso que imagens otimizadas também impactam positivamente no ranking de seu site nos mecanismos de busca.

Uma solução, seria ter múltiplas resoluções disponíveis e servir tamanhos apropriados de imagens, dependendo das características de tela do dispositivo que estiver fazendo o acesso.

Carregar Imagens de Acordo com a Resolução de Tela do Dispositivo do Usuário

Antes de mostrar de fato no código como carregar as imagens baseadas na resolução de tela dos dispositivos, precisamos entender o cenário e alguns conceitos no que diz respeito às telas.

Iniciando pela resolução, para aqueles que talvez não tenham esse conceito bem claro, ela pode ser entendida como a densidade de pixels presentes na tela. A densidade refere-se à quantidade de pixels apertados em um espaço físico, normalmente, uma polegada. Sendo representada pela sigla PPI (pixels per inch, pixels por polegada). Fazendo uma analogia, seria algo como população por metro quadrado. Já o pixel, em uma linguagem mais coloquial, é o menor quadradinho físico em sua tela, e que recebe uma cor.

As telas sempre evoluíram, em toda a história da computação. Mas um novo capítulo dessa evolução começou a ser escrito no ano de 2010, quando a Apple lançou seu iPhone 4 com uma tela de altíssima resolução, chamada por eles de Retina.

Retina é um termo criado pela Apple, para denominar telas que tenham uma densidade de pixels tão alta, que um ser humano com visão normal não consegue detectar pixels individuais a uma distância apropriada de visualização.

Existe uma distância recomendada entre o olho humano e a tela para cada dispositivo. No celular, por exemplo, ela é algo em torno de 30 centímetros, no computador, 40. Portanto, toda tela que se encaixe no conceito, pode ser chamada de Retina.

Como a evolução das telas tem levado essa densidade para níveis muito altos, temos cada vez mais pixels espremidos em uma polegada. Obviamente, para caber mais pixels em um mesmo espaço físico, os pixels precisam ficar menores.

Telas com diferentes tamanhos de pixels físicos geram o tipo de problema que estamos tentando descrever abaixo.

Temos inicialmente uma tela, onde renderizamos um objeto que ocupou, hipoteticamente, 24 pixels.

Tela com resolução 1x.

Como a próxima tela tem o dobro da resolução, o tamanho físico dos pixels caiu pela metade.

Se o tamanho dos pixels reduziu pela metade, ao renderizar a imagem nos mesmos 24 pixels, o objeto fica visualmente menor.

Tela com resolução 2x.

Como no mercado existem inúmeras resoluções de tela, para corrigir essas diferenças de apresentação, criou-se então um pixel referência, onde não necessariamente uma medida desse pixel corresponde a exatamente um pixel físico.

Essa medida recebe vários nomes: Device Independent Pixel na documentação do Android, na W3C, é chamada de CSS Pixel, e na Apple, de Dots. Outro nome, também encontrado em documentações, é Pixel Lógico.

Isso permite aos dispositivos, baseados em sua densidade, definir quantos pixels físicos realmente corresponderão a um pixel lógico, garantindo uma renderização padronizada em todas as resoluções. O que é bom, pois abstrai essa responsabilidade de nós, desenvolvedores.

A divisão entre a quantidade de pixels físicos pela de pixels lógicos de uma tela, gera uma nova métrica: o Device Pixel Ratio – DPR.

O DPR será necessário daqui a pouco, quando entrarmos na parte prática, para tomar decisões sobre nossas imagens. Informação que pode ser obtida com JavaScript, através da propriedade devicePixelRatio, ou acessada pelo CSS, conforme veremos mais para o final do artigo.

Device Pixel Ratio.

DPR de 1, significa que um pixel lógico corresponderá a um pixel físico. Já se a proporção for 2, por exemplo, um pixel lógico será desenhado utilizando-se o dobro de pixels físicos.

Quando tratamos de elementos vetoriais como textos e SVGs, tanto faz quantos pixels físicos serão realmente utilizados para desenhar cada elemento. Desde que a proporção seja mantida, está tudo certo.

Mas, ao lidar com imagens rasterizadas, as coisas não são bem assim. Já que esse tipo de imagem possui um número de pixels de largura e um número de pixels de altura, e começa a parecer granulada, quando forçada a se desenhar em uma quantidade maior de pixels do que a de seu tamanho original.

Em nosso exemplo acima, vamos assumir que a primeira tela tenha um DPR de 1 e a segunda, um DPR de 2. Para que o objeto desenhado na primeira tela permaneça com o mesmo tamanho visual na segunda, ele ocupará 4 vezes mais pixels.

Isso quer dizer que: se o objeto ocupou 24 pixels na primeira tela, será forçado a se renderizar em 96 pixels na segunda. Aí não tem mágica, cada um dos 24 pixels existentes originalmente na imagem, será esticado para o dobro de seu tamanho. Fazendo com que a imagem pareça embaçada, pois não há dados suficientes para que ela seja exibida corretamente. É daí que vem o termo, encontrado em artigos falando sobre o assunto, de que a tela retina explode o pixel.

Portanto, existe apenas uma solução para esse caso: dobrar o tamanho da imagem.

Para implementar essa ideia, poderíamos continuar disponibilizando nossas imagens originais em dispositivos com telas normais, de 1 DPR por exemplo, e gerar uma nova imagem, com o dobro das dimensões, para telas 2 DPRs e maiores.

Sabendo de tudo isso, agora estamos prontos para colocar a mão na massa, e implementar uma solução que irá carregar imagens de acordo com a resolução de tela do dispositivo do usuário.

Inserindo as Imagens via CSS

Quando você deseja inserir uma imagem somente decorativa em seu site, é aconselhável que o faça via CSS, para não poluir o HTML com elementos que só estariam ali por questões visuais. Isso se chama semântica de HTML, mas é assunto para outro artigo.

Então para telas comuns, simplesmente importamos nossa imagem original, que poderia ser algo como o mostrado na tira de código abaixo.

.site-panorama {
  background-image: url(imagens/panorama.jpg);
}

E, para telas Retina, envolvemos o CSS que importa nossa imagem preparada para esse tipo de tela em uma media query min-resolution, com o valor 2dppx. Isso faz com que o código dentro da media query só seja executado para situações onde o DPR for 2 ou maior.

@media (min-resolution: 2dppx) {
  .site-panorama {
      background-image: url(imagens/panorama@2x.jpg);
  }
}

Dessa forma, nossa imagem@2x só será carregada para telas retina. Aliás, essa forma de nomear imagens, colocando o arroba seguido do DPR surgiu na Apple, e hoje é bastante comum de se encontrar em diversos sites por aí. Ela ajuda a organizar melhor nossas imagens, identificando suas finalidades.

O fato de você ter dobrado as dimensões de sua imagem não quer dizer que ela vai se renderizar com a metade do tamanho em telas retina, ficando do mesmo tamanho da imagem original numa tela 1x. Ela agora terá o dobro do tamanho visual em qualquer tela, por conta da história do pixel referência visto acima.

Você precisa forçar ela a se renderizar com a metade do tamanho. Por que aí, em vez de o sistema operacional do dispositivo com tela retina esticar os pixels, simplesmente todos eles serão ocupados.

Uma forma, é colocar o background size de sua imagem @2x com a metade das dimensões. Por exemplo: se a imagem que você preparou para telas retina (dobrando suas dimensões) agora possui mil por mil pixels, faça com que ela se renderize em quinhentos por quinhentos.

background-size: 500px 500px;

Inserindo as Imagens Através do HTML

Nos casos em que a imagem realmente é parte do conteúdo e você deseja adicioná-la pela tag img, há algumas possibilidades.

Uma delas é o srcset, que surgiu como um novo atributo na tag img, sendo uma ferramenta a mais disponível para se trabalhar com web design responsivo em diferentes tamanhos e resoluções de tela. Ele nos permite trocar o src da imagem, e assim, carregar uma imagem específica de acordo com as características da tela do dispositivo.

No srcset, informamos uma lista, separada por vírgula, contendo o caminho da imagem e a resolução na qual ela deve ser apresentada.

No entanto, você não deve esquecer de fornecer o atributo src, como já se fazia antigamente. Assim, os navegadores que não suportam srcset também receberão uma imagem.

Com o srcset, não precisamos mais dimensionar a imagem. Pois isso já é feito automaticamente, através da informação de resolução, aquele 1x, 2x após o caminho. Fazendo com que ela tenha sempre o mesmo tamanho.

Como no mercado existem telas das mais variadas resoluções, num cenário onde eu tenha como opções em um srcset, preparada uma imagem 1x e outra 2x, quando um dispositivo com DPR de 1.5 fizer o acesso, será servido com a imagem 2x. Da mesma forma, considerando ainda essas duas opções de imagens, um dispositivo com DPR 3, receberá a imagem 2x.

Agora que já sabemos carregar imagens com base na resolução de uma tela, podemos dar uma passo adiante e aprender também a carregá-las considerando o tamanho, isso é, as dimensões de uma tela.

Decidir pela melhor imagem considerando o tamanho de tela do dispositivo

Carregar determinada imagem, dependendo do tamanho de tela do dispositivo, é chamado pelo mercado de direção de arte. Afinal, é o caso de o diretor artístico do site definir qual recorte, por exemplo de uma imagem, se encaixa melhor para aquelas dimensões de tela.

O elemento picture, que surgiu recentemente, foi pensado justamente para esses casos. Dentro dele, colocamos uma lista de sources. Cada um composto por um conjunto de regras, e o endereço de uma imagem informada no atributo src.

A imagem do source em que as regras combinarem com as especificações da tela do dispositivo, será carregada e exibida na página.

Adiciona-se também dentro de picture, uma tag img. É nela que colocamos o texto alternativo (famosa tag alt), classes ou ids para seleção e o endereço de uma imagem, que será utilizada por navegadores que ainda não suportam a tag picture.

No exemplo acima, será analisado primeiro a media query min-width: 800px. Se ela combinar com a largura de tela do viewport do dispositivo, ou seja, 800 pixels ou maior, será carregado o panorama-full, de acordo com a resolução da tela.

Do contrário, os próximos sources serão analisados, até encontrar um que combine. Se nenhum estiver de acordo ou o navegador não suporta a tag picture, será adotada a imagem da tag img.

Imagem Única para Todas as Resoluções de Tela

O grande problema de se gerar imagens com o dobro das dimensões para atender telas Retina, é o fato de o tamanho do arquivo também aumentar muito.

Este inconveniente estimulou a comunidade de programadores e designers na busca de uma solução. E ela não demorou muito a surgir. Uma técnica chamada compressive images têm sido bastante aceita como solução razoável.

Para telas normais, não Retina, os designers já recomendam exportar as imagens do seu editor com uma qualidade de 80%. Isso porque, aqueles 20% retirados não seriam perceptíveis ao olho humano. Dessa forma, obtém-se um arquivo menor.

A técnica surgiu da seguinte constatação: uma imagem exportada com qualidade bem baixa, algo em torno de 20%, por exemplo, ao ser renderizada em uma tela Retina, onde os pixels são muito pequenos e estão bem próximos uns dos outros, fica visualmente nítida e os detalhes são preservados.

Dessa forma, mesmo a imagem possuindo o dobro das dimensões, como sua qualidade é baixa, o tamanho do arquivo fica parecido com o da imagem antes de ser redimensionada.

Uma vez que o tamanho do arquivo agora já não é mais um problema, podemos usar essa única imagem para todas as resoluções, através de uma tag img convencional. Não esquecendo de limitar a imagem para a metade de suas dimensões.

Assim, em uma tela retina, onde a densidade de pixels é muito alta, nem notamos a qualidade menor do JPEG. E na tela comum, o fato de o navegador apertar a imagem para a metade de suas dimensões, fará com que ela fique boa também, equivalente a uma imagem 1x de alta qualidade.

Esta sacada permitiu contornar o problema de imagens JPEG muito pesadas para que ficassem boas em telas retina. Mas, e os arquivos PNG? Eles não têm qualidade controlável como o JPEG, então não é possível adotar uma estratégia semelhante. Como fazer?

Muita coisa que se fazia antigamente com PNG, hoje em dia já é feita com SVG e Icon Fonts, que suportam retina automaticamente. É o caso de gráficos, desenhos, ícones, elementos de interface e logos.

Nos casos em que realmente preciso de um PNG, como em um print de tela por exemplo, estou optando por utilizar a variação de 8 bits (PNG-8), para imagens com até 256 cores.

O Sérgio Lopes em seu livro, recomenda servir, nesse caso, a imagem de acordo com a resolução de tela do usuário e comenta que tem preferido somente a versão retina para todas as telas. Eu testei os prints que tenho em PNG-8 nas telas 2, 1.5 e 1x. No meu caso, a diferença de qualidade não compensa onerar o usuário com uma imagem 2x, já que a imagem 1x se mostrou satisfatória nas resoluções de telas em que testei.

Falando em Sérgio Lopes, esse é um livro que me ajudou demais, e você pode fazer a leitura dele, iniciando seu mês grátis na Amazon Kindle Unlimited.

Artigos Relacionados

Ir para o topo