Sintaxes descritivas

Neste módulo, você aprenderá a oferecer opções de imagens ao navegador para que ele possa tomar as melhores decisões sobre o que exibir. srcset não é um método para trocar origens de imagem em pontos de interrupção específicos, nem foi feito para trocar uma imagem por outra. Essas sintaxes permitem que a navegador para solucionar um problema muito difícil, sem depender de nós: solicitar e renderizar facilmente uma fonte de imagem adaptada ao contexto de navegação do usuário, incluindo o tamanho da janela de visualização, a densidade da tela, as preferências do usuário, a largura de banda e inúmeros outros fatores.

É uma grande solicitação, certamente mais do que queremos considerar quando estamos simplesmente marcando uma imagem para a web, e fazê-lo bem envolve mais mais informações do que podemos acessar.

Descrevendo a densidade com x

Uma <img> com largura fixa ocupa a mesma quantidade da janela de visualização em qualquer contexto de navegação, independentemente da densidade da tela, o número de pixels físicos que compõem a tela. Por exemplo, uma imagem com uma largura inerente de 400px ocupará quase em toda a janela de visualização do navegador, tanto no Google Pixel original quanto no Pixel 6 Pro mais recente. Ambos os dispositivos têm um 412px pixel lógico da janela de visualização ampla.

No entanto, o Pixel 6 Pro tem uma tela muito mais nítida: o 6 Pro tem uma resolução física de 1.440 × 3.120 pixels, enquanto a O pixel tem 1080 × 1920 pixels, ou seja, o número de pixels de hardware que compõem a própria tela.

A proporção entre os pixels lógicos e físicos de um dispositivo é a proporção de pixels dessa tela (DPR, na sigla em inglês). A DPR é calculada dividindo a resolução real da tela do dispositivo pelos pixels CSS de uma janela de visualização.

Um DPR de 2 exibido em uma janela do console.

Portanto, o Pixel original tem uma DPR de 2,6, enquanto o Pixel 6 Pro tem uma DPR de 3,5.

O iPhone 4, o primeiro dispositivo com um DPR maior que 1, informa uma proporção de pixels de 2 — a resolução física da tela é de o dobro da resolução lógica. Todos os dispositivos anteriores ao iPhone 4 tinham um DPR de 1: um pixel lógico a um pixel físico.

Se você visualizar essa imagem de 400px em uma tela com um DPR de 2, cada pixel lógico está sendo renderizado em quatro das pixels físicos da tela: dois horizontais e dois verticais. A imagem não se beneficia da tela de alta densidade. A imagem terá a aparência assim como seria em uma tela com uma DPR de 1. Claro, qualquer coisa "desenhada" pelo mecanismo de renderização do navegador: texto, formas CSS ou SVGs, por exemplo, serão desenhadas para se adequar à exibição de densidade mais alta. No entanto, como você aprendeu em Formatos e compactação de imagens, as imagens rasterizadas são fixas grades de pixels. Embora isso nem sempre seja claramente óbvio, uma imagem rasterizada ampliada para se adequar a uma tela de densidade mais alta terá baixa resolução em comparação com a página ao redor.

Para evitar esse aumento, a imagem renderizada precisa ter uma largura intrínseca de pelo menos 800 pixels. Quando reduzida para caber em um espaço em um layout com 400 pixels lógicos de largura, essa origem da imagem de 800 pixels tem o dobro da densidade de pixels em uma tela com DPR de 2. ela ficará bonita e nítida.

Close de uma pétala de flor mostrando disparidade de densidade.

Como uma tela com um DPR de 1 não pode usar a densidade aumentada de uma imagem, ela será reduzida para corresponder e, como você sabe, uma imagem diminuída ficaria bem. Em uma tela de baixa densidade, uma imagem adequada para densidade mais alta e as telas serão parecidas com qualquer outra imagem de baixa densidade.

Como você aprendeu em Imagens e desempenho, um usuário com uma tela de baixa densidade visualizando uma origem de imagem reduzida para 400pxprecisarão de uma fonte com largura inerente de 400px. Embora uma imagem muito maior funcione para todos os usuários visualmente, uma imagem A origem de imagens de alta resolução renderizada em uma tela pequena e de baixa densidade é semelhante a qualquer outra imagem pequena e de baixa densidade, mas parece muito mais lenta.

Como você pode imaginar, dispositivos móveis com uma DPR de 1 são muito raros, embora ainda seja comum em computadores em contextos de navegação. De acordo com os dados compartilhada por Matt Hobbs, aproximadamente 18% das sessões de navegação do GOV.UK em novembro de 2022 informaram uma DPR de 1. Embora imagens de alta densidade tenham a aparência esperada pelos usuários, elas têm uma largura de banda e um custo de processamento muito maiores Os usuários de dispositivos mais antigos e menos potentes ainda são uma grande preocupação para os usuários que têm telas de baixa densidade.

O uso de srcset garante que apenas dispositivos com telas de alta resolução recebam fontes de imagem grandes o suficiente para parecerem nítidas, sem transmitir o mesmo custos de largura de banda junto aos usuários com telas de resolução mais baixa.

O atributo srcset identifica um ou mais candidatos separados por vírgula para renderizar uma imagem. Cada candidato é composto de duas coisas: um URL, como você usaria em src, e uma sintaxe que descreve essa origem da imagem. Cada candidato em srcset é descrita pela largura inerente ("sintaxe w") ou a densidade pretendida ("sintaxe x").

A sintaxe x é uma abreviação de "essa fonte é apropriada para uma exibição com esta densidade". Um candidato seguido por 2x é apropriada para uma tela com um DPR de 2.

<img src="low-density.jpg" srcset="double-density.jpg 2x" alt="...">

Os navegadores compatíveis com srcset vão receber dois candidatos: double-density.jpg, que 2x descreve conforme apropriado. para telas com um DPR de 2 e low-density.jpg no atributo src: o candidato selecionado se nada mais apropriado for encontrado em srcset. Para navegadores sem suporte a srcset, o atributo e o conteúdo dele serão ignorados. O conteúdo de src será ignorado. será solicitado, como de costume.

É fácil confundir os valores especificados no atributo srcset para instruções. Esse 2x informa ao navegador que a arquivo de origem associado seria adequado para uso em um monitor com DPR de 2, ou seja, informações sobre a própria fonte. Não diz o navegador como usar essa fonte, apenas informa ao navegador como a fonte pode ser usada. É uma distinção sutil, mas importante: é uma imagem de dupla densidade, não uma imagem para uso em uma tela de dupla densidade.

A diferença entre uma sintaxe que diz "essa fonte é adequada para telas 2x" e outra com a mensagem "Usar esta fonte em telas 2x" é pequeno em materiais impressos, mas a densidade de exibição é apenas um dos inúmeros fatores interligados que o navegador usa para decidir sobre o candidato para renderizar, mas apenas alguns dos quais você pode conhecer. Por exemplo: individualmente, é possível determinar que um usuário ativou uma preferência do navegador de economia de largura de banda por meio da consulta de mídia prefers-reduced-data e use-a para sempre permitir que os usuários escolham imagens de baixa densidade. independentemente da densidade de exibição. No entanto, a menos que seja implementado de forma consistente por todos os desenvolvedores e em todos os sites, ele não seria muito útil para o usuário. Eles podem ter sua preferência respeitada em um site e se deparar com um muro de imagens que reduz a largura de banda no outro.

O algoritmo de seleção de recursos deliberadamente vago usado por srcset/sizes deixa espaço para os navegadores escolherem a densidade mais baixa imagens com quedas de largura de banda ou com base na preferência de minimizar o uso de dados, sem assumirmos a responsabilidade por como, quando ou qual limite. Não faz sentido assumir responsabilidades e tarefas adicionais que o navegador está mais bem equipado para lidar com você.

Como descrever larguras com w

srcset aceita um segundo tipo de descritor para candidatos à fonte de imagem. Ela é muito mais eficiente e, para nossos propósitos, muito mais fácil de entender. Em vez de sinalizar um candidato como tendo as dimensões adequadas para uma determinada densidade de exibição, a sintaxe w descreve a largura inerente de cada fonte candidata. Cada candidato é idêntico, exceto em suas dimensões - as mesmas conteúdo, o mesmo corte e a mesma proporção. Mas neste caso, você quer que o navegador do usuário escolha entre dois candidatos: small.jpg, uma origem com uma largura inerente de 600px, e Large.jpg, uma fonte com uma largura inerente de 1200px.

srcset="small.jpg 600w, large.jpg 1200w"

Isso não diz ao navegador o que fazer com essas informações, apenas fornece uma lista de candidatos para exibir a imagem. Antes que o navegador possa tomar uma decisão sobre qual fonte renderizar, você precisa fornecer mais algumas informações: descrição de como a imagem será renderizada na página. Para fazer isso, use o atributo sizes.

Como descrever o uso com sizes

Os navegadores têm um desempenho incrível quando se trata de transferir imagens. As solicitações de recursos de imagem serão iniciadas por um período longo antes das solicitações de folhas de estilo ou de JavaScript. Muitas vezes, até mesmo antes da marcação ser totalmente analisada. Quando o navegador faz essas solicitações, ele não tem informações sobre a página em si, além da marcação; pode nem mesmo ter iniciado solicitações para folhas de estilo externas, e menos ainda as aplicamos. No momento em que o navegador analisa sua marcação e começa a fazer alterações ele tem apenas informações do navegador: o tamanho da janela de visualização do usuário, a densidade de pixels da tela do usuário, preferências do usuário e assim por diante.

Isso não diz nada sobre como uma imagem deve ser renderizada no layout da página, porque não é possível usar a janela de visualização. como proxy do limite superior do tamanho de img, já que ele pode ocupar um contêiner de rolagem horizontal. Portanto, precisamos forneça essas informações ao navegador e faça isso usando marcações. Isso é tudo que poderemos usar para essas solicitações.

Assim como srcset, sizes visa disponibilizar informações sobre uma imagem assim que a marcação é analisada. Assim como o srcset atributo é uma abreviação de "aqui estão os arquivos de origem e seus tamanhos inerentes", o atributo sizes é uma abreviação de "aqui é o tamanho da imagem renderizada no layout." A forma como você descreve a imagem é relativa à janela de visualização. O tamanho é a única informação de layout que o navegador tem quando a solicitação de imagem é feita.

Isso pode parecer um pouco complicado na mídia impressa, mas é muito mais fácil de entender na prática:

<img
 sizes="80vw"
 srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
 src="fallback.jpg"
 alt="...">

Aqui, esse valor de sizes informa ao navegador que o espaço no layout que o img ocupa tem uma largura de 80vw a 80% do na janela de visualização. Lembre-se de que isso não é uma instrução, mas uma descrição do tamanho da imagem no layout da página. Não diz "faça isto imagem ocupem 80% da janela de visualização", mas "essa imagem acabará ocupando 80% da janela de visualização após a renderização da página".

Como desenvolvedor, seu trabalho está concluído. Você descreveu com precisão uma lista de origens candidatas em srcset e a largura da sua imagem em sizes e, assim como na sintaxe x em srcset, o restante é tarefa do navegador.

Mas, para entender completamente como essas informações são usadas, vamos analisar as decisões que navegador de um usuário faz ao encontrar esta marcação:

Você informou ao navegador que esta imagem vai ocupar 80% da janela de visualização disponível. Portanto, se renderizarmos img em uma com uma janela de visualização de 1.000 pixels de largura, essa imagem ocupará 800 pixels. O navegador então pegará esse valor e dividirá a largura de cada um dos candidatos de origem de imagem especificados em srcset. A menor fonte tem um tamanho inerente de 600 pixels, Portanto: 600÷800=0,75. Nossa imagem média tem 1.200 pixels de largura: 1.200 ÷ 800=1,5. Nossa maior imagem tem 2.000 pixels de largura: 2000÷800=2,5.

Os resultados desses cálculos (.75, 1.5 e 2.5) são opções da DPR especificamente adaptadas tamanho da janela de visualização. Como o navegador também tem informações sobre a densidade de exibição do usuário, ele toma uma série de decisões:

Neste tamanho da janela de visualização, o candidato small.jpg é descartado, independentemente da densidade de exibição do usuário, com uma DPR calculada menor que 1, essa fonte exigiria ampliação para qualquer usuário, portanto, não é apropriada. Em um dispositivo com uma DPR de 1, o medium.jpg fornece a correspondência mais próxima: essa fonte é apropriada para exibição com um DPR de 1.5, portanto, é um pouco maior do que o necessário, mas lembre-se de que a redução de escalonamento é um processo visualmente perfeito. Em um dispositivo com uma DPR de 2,large.jpg é a correspondência mais próxima, por isso é selecionado.

Se a mesma imagem for renderizada em uma janela de visualização de 600 pixels de largura, o resultado de todos esses cálculos será completamente diferente: 80 vw agora é de 480 px. Quando dividimos as fontes em relação a isso, temos 1.25, 2.5 e 4.1666666667. Neste tamanho da janela de visualização, small.jpg será escolhido em dispositivos 1x e medium.jpg vai corresponder em dispositivos de 2x.

Essa imagem será idêntica em todos esses contextos de navegação: todos os nossos arquivos de origem são exatamente iguais, exceto pelas dimensões, e cada uma delas é renderizada com a maior qualidade possível conforme a densidade de exibição do usuário permite. No entanto, em vez de exibir large.jpg para todos os usuários, Para acomodar as maiores janelas de visualização e telas de maior densidade, os usuários sempre receberão o menor candidato adequado. Ao usar uma sintaxe descritiva em vez de uma prescritiva, você não precisa definir pontos de interrupção manualmente e considerar futuras janelas de visualização e DPRs: basta fornecer ao navegador informações e permitir que ele determine as respostas para você.

Como nosso valor de sizes é relativo à janela de visualização e completamente independente do layout da página, ele adiciona uma camada de complicação. É raro ter uma imagem que ocupe apenas uma porcentagem da janela de visualização, sem margens de largura fixa, preenchimento ou influência de outros elementos na página. Muitas vezes, você precisará expressar a largura de uma imagem usando uma combinação de unidades. porcentagens, em, px e assim por diante.

Você pode usar calc() aqui. Qualquer navegador com suporte nativo para imagens responsivas também é compatível com calc(), permitindo misturam e correspondem unidades CSS, por exemplo, uma imagem que ocupa toda a largura da janela de visualização do usuário, menos uma margem de 1em em ambos os lados:

<img
    sizes="calc(100vw-2em)"
    srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1600w, x-large.jpg 2400w"
    src="fallback.jpg"
    alt="...">

Como descrever pontos de interrupção

Se você passou muito tempo trabalhando com layouts responsivos, provavelmente notou que falta algo nesses exemplos: o espaço que uma imagem ocupa em um layout tem muita probabilidade de mudar nos pontos de interrupção dele. Nesse caso, você precisa para transmitir um pouco mais de detalhes ao navegador: sizes aceita um conjunto de candidatos separados por vírgulas para o tamanho renderizado do imagem, assim como srcset aceita candidatos separados por vírgulas para origens de imagem. Essas condições usam a conhecida sintaxe de consulta de mídia. Essa sintaxe é a primeira correspondência: assim que uma condição de mídia é correspondida, o navegador para de analisar o atributo sizes, e o valor especificado é aplicado.

Digamos que você tenha uma imagem destinada a ocupar 80% da janela de visualização, menos um em de padding em ambos os lados, em janelas de visualização acima de 1.200 px, em janelas de visualização menores, ele ocupa toda a largura da janela de visualização.

  <img
     sizes="(min-width: 1200px) calc(80vw - 2em), 100vw"
     srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
     src="fallback.jpg"
     alt="...">

Se a janela de visualização do usuário for maior que 1.200 px, calc(80vw - 2em) descreve a largura da imagem no nosso layout. Se o A condição (min-width: 1200px) não corresponde, o navegador avança para o próximo valor. Como não há um valor específico condição de mídia vinculada a esse valor, o padrão é 100vw. Se você escrevesse o atributo sizes usando max-width consultas de mídia:

  <img
     sizes="(max-width: 1200px) 100vw, calc(80vw - 2em)"
     srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
     src="fallback.jpg"
     alt="...">

Em linguagem simples: "(max-width: 1200px) é correspondente? Caso contrário, siga em frente. O próximo valor, calc(80vw - 2em), não tem condição de qualificação. então este é o selecionado.

Agora que você forneceu ao navegador todas as informações sobre seu elemento img: possíveis origens, larguras inerentes, e como você pretende apresentar a imagem ao usuário. O navegador usa um conjunto impreciso de regras para determinar o que fazer com essas informações. Se isso soa vago, é porque é - por padrão. O algoritmo de seleção de origem codificado no A especificação HTML é explicitamente vaga sobre como uma fonte deve ser escolhida. Uma vez que as fontes, os descritores delas a imagem será renderizada depois de ser analisada, o navegador estará livre para fazer o que quiser. Não é possível saber ao certo qual origem escolhida pelo navegador.

Uma sintaxe que diz "use esta fonte em uma tela de alta resolução" seria previsível, mas não resolveria o problema principal com imagens em um layout responsivo: conservando a largura de banda do usuário. A densidade de pixels de uma tela só está relacionada à Internet de maneira tangencial a velocidade da conexão, se houver. Se você estiver usando um laptop top de linha, mas navegando na Web por uma conexão limitada, ele será vinculado ao celular ou usando uma conexão Wi-Fi de avião instável. Talvez seja melhor desativar as fontes de imagem de alta resolução, a qualidade da tela.

Deixar a palavra final para o navegador permite melhorias de desempenho muito mais do que poderíamos gerenciar com uma abordagem estritamente prescritiva . Por exemplo: na maioria dos navegadores, um img que usa a sintaxe srcset ou sizes nunca solicitará uma origem com menor do que aquela que o usuário já tem no cache do navegador. Qual seria o sentido de fazer uma nova solicitação para uma origem? que seriam idênticos se o navegador pudesse reduzir a origem da imagem que ele já tem? Mas, se o usuário dimensiona da janela de visualização até o ponto em que uma nova imagem é necessária para evitar a ampliação, essa solicitação ainda será feita, então tudo tenha a aparência esperada.

Essa falta de controle explícito pode parecer um pouco assustadora, mas como você usa arquivos de origem com arquivos não é mais provável que sejamos apresentados aos usuários do que com uma src de origem única, independente da as decisões tomadas pelo navegador.

Como usar sizes e srcset

É muita informação, tanto para você, o leitor, quanto para o navegador. srcset e sizes são sintaxes densas, descrevendo uma quantidade chocante de informações em relativamente poucos caracteres. Ou seja, para o bem ou para o mal, por padrão: essas sintaxes menos concisas e mais facilmente analisadas por humanos, poderiam ter dificultado a análise por navegadores. A mais complexidade é adicionada a uma string, maior o potencial de erros do analisador ou diferenças não intencionais de comportamento de um navegador para outro. No entanto, há uma vantagem: uma sintaxe lida mais facilmente pelas máquinas é uma sintaxe escrita mais facilmente por eles.

srcset é um caso claro de automação. É raro você criar à mão várias versões de suas imagens para um ambiente de produção, automatizando o processo usando um executor de tarefas como o Gulp, um bundler como o Webpack, uma empresa terceirizada CDN, como Cloudinary, ou funcionalidades já integradas ao CMS de sua preferência. Ter recebido informações suficientes para gerar nossas fontes em primeiro lugar, um sistema teria informações suficientes para gravá-las em um atributo srcset viável.

sizes é um pouco mais difícil de automatizar. Como você sabe, a única maneira de um sistema calcular o tamanho de uma imagem layout renderizado é renderizar o layout. Felizmente, surgiram várias ferramentas para desenvolvedores para abstrair o processo de escrever à mão atributos sizes, com uma eficiência que nunca seria possível igualar manualmente. respImageLint, por exemplo, é um snippet de código destinado a verificar os atributos sizes. para garantir a precisão e fornecer sugestões de melhoria. O projeto Lazysizes compromete alguma velocidade para aumentar a eficiência, adiando as solicitações de imagem até que o layout tenha sido estabelecido, permitindo que o JavaScript gerar valores sizes para você. Se você estiver usando um framework de renderização totalmente do lado do cliente, como React ou Vue, há uma número de soluções para criação e/ou geração de atributos srcset e sizes, que vamos discutir mais em CMS e Frameworks.