Sintaxis descriptivas

En este módulo, aprenderás a darle al navegador la opción de elegir algunas imágenes para que pueda tomar las mejores decisiones sobre qué mostrar. srcset no es un método para intercambiar fuentes de imágenes en puntos de interrupción específicos ni está diseñado para cambiar una imagen por otra. Estas sintaxis permiten que el modelo para resolver un problema muy difícil, independientemente de nosotros: solicitar y mostrar sin problemas una fuente de imágenes adaptada al contexto de navegación del usuario, incluidos el tamaño del viewport, la densidad de la pantalla, las preferencias del usuario, el ancho de banda y muchos otros factores.

Es una gran pregunta, ciertamente más de la que queremos tener en cuenta cuando solo marcamos una imagen para la Web, y hacerlo bien implica más información a la que tenemos acceso.

Describe la densidad con x

Un objeto <img> con un ancho fijo ocupará la misma cantidad del viewport en cualquier contexto de navegación, independientemente de la densidad de la pantalla: la cantidad de píxeles físicos que componen la pantalla. Por ejemplo, una imagen con un ancho inherente de 400px ocupará casi todo el viewport del navegador, tanto en el Google Pixel original como en el Pixel 6 Pro más reciente; ambos dispositivos tienen una 412px normalizada logical pixel ancho de vista.

Sin embargo, el Pixel 6 Pro tiene una pantalla mucho más nítida: el 6 Pro tiene una resolución física de 1,440 × 3,120 píxeles, mientras que el Los píxeles son de 1080 × 1920 píxeles, es decir, la cantidad de píxeles de hardware que conforman la pantalla.

La relación entre los píxeles lógicos de un dispositivo y los píxeles físicos es la relación de píxeles del dispositivo para esa pantalla (DPR). La DPR es se calcula dividiendo la resolución real de la pantalla del dispositivo por los píxeles CSS de una viewport.

Se muestra una DPR de 2 en una ventana de la consola.

Por lo tanto, el Pixel original tiene una DPR de 2.6, mientras que el Pixel 6 Pro tiene una DPR de 3.5.

El iPhone 4, el primer dispositivo con una DPR superior a 1, informa una relación de píxeles del dispositivo de 2, es decir, la resolución física de la pantalla es el doble de resolución lógica. Todos los dispositivos anteriores al iPhone 4 tenían una DPR de 1, es decir, de un píxel lógico a un píxel físico.

Si ves esa imagen de ancho de 400px en una pantalla con una DPR de 2, cada píxel lógico se renderiza en cuatro de píxeles físicos de la pantalla: dos horizontales y dos verticales. La pantalla de alta densidad no beneficia a la imagen, ya que se verá igual que en una pantalla con una DPR de 1. Por supuesto, cualquier cosa "dibujada" por el motor de renderizado del navegador (texto, formas CSS o SVG), por ejemplo, se dibujarán para adaptarse a la pantalla de mayor densidad. Sin embargo, como aprendiste en Formatos de imagen y compresión, las imágenes de trama son fijas cuadrículas de píxeles. Si bien no siempre es evidente, el aspecto de una imagen de trama aumentada para adaptarse a una pantalla de mayor densidad de baja resolución en comparación con la página circundante.

Para evitar este aumento de tamaño, la imagen que se renderiza debe tener un ancho intrínseco de al menos 800 píxeles. Cuando se reduce la escala para adaptarse a un espacio en un diseño de 400 píxeles lógicos de ancho, esa fuente de imagen de 800 píxeles tiene el doble de densidad de píxeles, en una pantalla con una DPR de 2, se verá bien y nítido.

Primer plano del pétalo de una flor que muestra la disparidad en densidad.

Debido a que una pantalla con una DPR de 1 no puede usar la mayor densidad de una imagen, se ajustará su escala para que coincida con la y, como sabes, una imagen redimensionada se verá bien. En una pantalla de baja densidad, una imagen adecuada para una densidad más alta se verán como cualquier otra imagen de baja densidad.

Como aprendiste en Imágenes y rendimiento, un usuario con una pantalla de baja densidad mirando una fuente de imagen reducida a 400px Solo necesitará una fuente con un ancho inherente de 400px. Si bien una imagen mucho más grande funcionaría visualmente para todos los usuarios, una La fuente de la imagen de alta resolución renderizada en una pantalla pequeña de baja densidad se verá como cualquier otra imagen pequeña de baja densidad, pero se sentirá mucho más lenta.

Como puedes suponer, los dispositivos móviles con una DPR de 1 son vanamente raros, aunque todavía es común en "computadoras de escritorio" de navegación. Según los datos compartido por Matt Hobbs, aproximadamente el 18% de las sesiones de navegación del GOV.UK de noviembre de 2022 informan una DPR de 1. Si bien las imágenes de alta densidad se verían del modo en que los usuarios podrían esperar, tendrán un ancho de banda y un costo de procesamiento mucho mayores particular preocupación a los usuarios de dispositivos más antiguos y menos potentes que siguen teniendo pantallas de baja densidad.

El uso de srcset garantiza que solo los dispositivos con pantallas de alta resolución reciban fuentes de imágenes lo suficientemente grandes como para verse nítidas, sin pasar las mismas de ancho de banda junto a los usuarios con pantallas de menor resolución.

El atributo srcset identifica uno o más candidatos separados por comas para renderizar una imagen. Cada candidato está conformado por dos cosas: una URL, como lo usarías en src, y una sintaxis que describe esa fuente de imagen. Cada candidato en srcset se describe por su ancho inherente ("sintaxis w") o densidad prevista ("sintaxis x").

La sintaxis x es una abreviatura de "esta fuente es adecuada para una pantalla con esta densidad", un candidato seguido de 2x es apropiado para una pantalla con una DPR de 2.

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

Los navegadores compatibles con srcset mostrarán dos opciones: double-density.jpg, que 2x describe como apropiada. para pantallas con una DPR de 2 y low-density.jpg en el atributo src (el candidato seleccionado si no hay nada más apropiado) que se encuentran en srcset. En el caso de los navegadores que no admiten srcset, se ignorará el atributo y su contenido (el contenido de src). como de costumbre.

Es fácil confundir los valores especificados en el atributo srcset con las instrucciones. Ese 2x informa al navegador que el elemento el archivo de origen asociado sería adecuado para su uso en una pantalla con una DPR de 2 (información sobre la fuente misma). No indica al navegador cómo utilizar esa fuente, simplemente informa al navegador cómo se puede utilizar la fuente. Es una distinción sutil, pero importante: Es una imagen de doble densidad, no una imagen para usar en una pantalla de doble densidad.

La diferencia entre una sintaxis que dice "esta fuente es adecuada para pantallas 2x" y una que dice "Usar esta fuente en pantallas 2x" La versión impresa es leve, pero la densidad de la pantalla es solo uno de una gran cantidad de factores intervinculados que el navegador utiliza para decidir sobre el candidato. solo algunas de las cuales puedes conocer. Por ejemplo, puedes determinar individualmente que un usuario habilitó un preferencia de navegador que ahorra ancho de banda a través de la consulta de medios prefers-reduced-data y usarla para que los usuarios siempre reciban imágenes de baja densidad. independientemente de su densidad de visualización, pero a menos que se implemente de forma coherente por parte de todos los desarrolladores en todos los sitios web, no sería de mucha utilidad para el usuario. Es posible que se respete su preferencia en un sitio y que, en el siguiente, se encuentren con un muro de imágenes que obstruye el ancho de banda.

El algoritmo de selección de recursos deliberadamente impreciso que usan srcset y sizes deja lugar a los navegadores para que elijan una densidad más baja. con pérdidas de ancho de banda o en función de la preferencia de minimizar el uso de datos, sin que nos hagamos responsable de cómo, cuándo o al a qué umbral. No tiene sentido asumir responsabilidades (y trabajo adicional) que el navegador esté mejor preparado para ti.

Describe los anchos con w

srcset acepta un segundo tipo de descriptor para los candidatos de fuentes de imágenes. Es mucho más poderosa, y para nuestros propósitos, mucho más fácil de entender. En lugar de marcar un candidato con las dimensiones adecuadas para una densidad de visualización determinada, La sintaxis w describe el ancho inherente de cada fuente candidata. Una vez más, cada candidato es idéntico, salvo por sus dimensiones: el mismo el contenido, el mismo recorte y la misma relación de aspecto. Pero, en este caso, quieres que el navegador del usuario elija entre dos opciones: small.jpg, una fuente con un ancho inherente de 600 px, y large.jpg, una fuente con un ancho inherente de 1200 px.

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

Esto no le indica al navegador qué hacer con esta información, solo le proporciona una lista de candidatos para mostrar la imagen. Antes de que el navegador pueda decidir qué fuente renderizar, debes proporcionarle un poco más de información: descripción de cómo se renderizará la imagen en la página. Para ello, usa el atributo sizes.

Describe el uso con sizes

Los navegadores son excelentes para transferir imágenes. Las solicitudes de recursos de imagen se iniciarán durante un tiempo prolongado antes de las solicitudes de hojas de estilo o JavaScript (a menudo, incluso antes de que el lenguaje de marcado se haya analizado por completo). Cuando el navegador realiza estas solicitudes, no tiene información sobre la página en sí, además del lenguaje de marcado (es posible que no haya iniciado solicitudes) para las hojas de estilo externas, y mucho menos para aplicarlas. Cuando el navegador analiza el lenguaje de marcado y comienza a crear solo tiene información a nivel del navegador: el tamaño del viewport del usuario, la densidad de píxeles de la pantalla del usuario, las preferencias del usuario, etcétera.

Esto no nos dice nada sobre cómo se pretende que una imagen se renderice en el diseño de la página; ni siquiera puede usar el viewport. como un proxy para el límite superior del tamaño de img, ya que puede ocupar un contenedor de desplazamiento horizontal. Así que necesitamos proporcionarle esta información al navegador y usar lenguaje de marcado. Eso es todo lo que podremos usar para estas solicitudes.

Al igual que srcset, el objetivo de sizes es que la información sobre una imagen esté disponible en cuanto se analiza el lenguaje de marcado. Al igual que el srcset es la abreviatura de “aquí están los archivos de origen y sus tamaños inherentes”, el atributo sizes es la abreviatura de "here" es el tamaño de la imagen renderizada en el diseño." La forma en que describes la imagen es relativa a la viewport el tamaño es la única información de diseño que tiene el navegador cuando se realiza la solicitud de imagen.

Esto puede sonar un poco complicado en el uso impreso, pero es mucho más fácil de entender en la práctica:

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

Aquí, este valor sizes informa al navegador que el espacio en nuestro diseño que ocupa img tiene un ancho entre 80vw y el 80% de el viewport. Recuerda que esta no es una instrucción, sino una descripción del tamaño de la imagen en el diseño de la página. No dice ocupan el 80% de la vista del puerto" pero "esta imagen terminará ocupando el 80% del viewport una vez que se haya renderizado la página".

Como desarrollador, tu trabajo está terminado. Describiste con precisión una lista de fuentes candidatas en srcset y el ancho de tu imagen en sizes y, al igual que con la sintaxis x en srcset, el resto depende del navegador.

Pero para comprender en profundidad cómo se usa esta información, tomémonos un momento para repasar las decisiones que que realiza el navegador de un usuario al encontrar este lenguaje de marcado:

Informaste al navegador que esta imagen ocupará el 80% del viewport disponible. Por lo tanto, si renderizamos este img en un dispositivo con un viewport de 1000 píxeles de ancho, esta imagen ocupará 800 píxeles. El navegador tomará ese valor y lo dividirá que indica el ancho de cada una de las opciones de fuente de imágenes que especificamos en srcset. La fuente más pequeña tiene un tamaño inherente de 600 píxeles así: 600 ÷ 800 = 0.75. Nuestra imagen mediana tiene 1,200 píxeles de ancho: 1,200 ÷ 800=1.5. Nuestra imagen más grande es de 2,000 píxeles de ancho: 2,000 ÷ 800=2.5.

Los resultados de esos cálculos (.75, 1.5 y 2.5) son opciones de DPR específicas para el usuario tamaño de viewport. Debido a que el navegador también dispone de información sobre la densidad de la pantalla del usuario, toma una serie de decisiones:

En este tamaño de viewport, el candidato small.jpg se descarta, independientemente de la densidad de visualización del usuario, con una DPR calculada más baja. que 1, esta fuente requeriría un aumento de la escala para cualquier usuario, por lo que no es adecuada. En un dispositivo con una DPR de 1, medium.jpg proporciona la coincidencia más cercana: esa fuente es adecuada para mostrarse en una DPR de 1.5, por lo que es un poco más grande de lo necesario, pero recuerda que la reducción de la escala es un proceso visualmente fluido. En un dispositivo con una DPR de 2,large.jpg es la coincidencia más cercana, por lo que se selecciona.

Si la misma imagen se renderiza en un viewport de 600 píxeles de ancho, el resultado de todos esos cálculos matemáticos sería completamente diferente: ahora 80vw es de 480 px. Cuando dividimos nuestras fuentes anchos con respecto a eso, obtenemos 1.25, 2.5 y 4.1666666667. Para este tamaño de viewport, se elegirán small.jpg en dispositivos 1x, y medium.jpg coincidirá en dispositivos 2x.

Esta imagen se verá idéntica en todos estos contextos de navegación: todos nuestros archivos fuente son exactamente iguales, independientemente de sus dimensiones. y cada una se renderiza con la nitidez que permite la densidad de la pantalla del usuario. Sin embargo, en lugar de entregar large.jpg a cada usuario Para adaptarse a las vistas del puerto más grandes y a las pantallas de mayor densidad, los usuarios siempre verán la versión más pequeña posible. Si usas una sintaxis descriptiva en lugar de prescriptiva, no es necesario establecer manualmente los puntos de interrupción ni considerar futuras ventanas de visualización y DPR: solo debes proporcionar información al navegador y permitirle que determine las respuestas por ti.

Como el valor sizes es relativo al viewport y es completamente independiente del diseño de la página, agrega una capa de complicación. No es común tener una imagen que solo ocupe un porcentaje del viewport, sin márgenes de ancho fijo, relleno ni influencia de otros elementos de la página. A menudo, necesitarás expresar el ancho de una imagen usando una combinación de unidades. porcentajes, em, px, etcétera.

Afortunadamente, puedes usar calc() aquí; cualquier navegador con compatibilidad nativa para imágenes responsivas también admitirá calc(), lo que nos permite Unidades de CSS combinadas (por ejemplo, una imagen que ocupa todo el ancho del viewport del usuario, menos un margen 1em a cada lado):

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

Describe los puntos de interrupción

Si has pasado mucho tiempo trabajando con diseños adaptables, es probable que hayas notado que falta algo en estos ejemplos: es muy probable que el espacio que ocupa una imagen en un diseño cambie entre los puntos de interrupción de nuestro diseño. En ese caso, necesitas para pasar un poco más de detalle al navegador: sizes acepta un conjunto separado por comas de candidatos para el tamaño renderizado de la del mismo modo que srcset acepta candidatos separados por comas para fuentes de imágenes. Esas condiciones usan la conocida sintaxis de consulta de medios. Esta sintaxis es de primera coincidencia: en cuanto una condición de contenido multimedia coincide, el navegador deja de analizar el atributo sizes y el valor. especificada.

Supongamos que tienes una imagen que ocupa el 80% del viewport, menos un em de padding a cada lado, en viewports de más de 1,200 px. o ventanas de visualización más pequeñas, ocupa todo el ancho de la vista del puerto.

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

Si el viewport del usuario supera los 1,200 px, calc(80vw - 2em) describe el ancho de la imagen en nuestro diseño. Si el botón La condición (min-width: 1200px) no coincide, el navegador pasa al siguiente valor. Debido a que no hay un condición de contenido multimedia vinculada a este valor, se usa 100vw como valor predeterminado. Si escribieras este atributo sizes con max-width consultas de medios:

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

Está en un lenguaje sencillo: "¿(max-width: 1200px) coincide? Si no es así, continúa. El siguiente valor (calc(80vw - 2em)) no tiene una condición que califique así que este es el seleccionado.

Ahora que proporcionaste al navegador toda la información sobre el elemento img (fuentes potenciales, anchos inherentes, y cómo pretendes presentar la imagen al usuario (el navegador utiliza un conjunto difuso de reglas para determinar qué hacer con esa información. Si eso suena poco claro, bueno, es porque lo es, de manera intencional. El algoritmo de selección de fuente codificado en la La especificación de HTML es explícitamente imprecisa sobre cómo se debe elegir una fuente. Una vez que las fuentes, sus descriptores se haya analizado la imagen que se renderizará, el navegador puede hacer lo que quiera (no puedes saber con seguridad qué fuente que elegirá el navegador.

Una sintaxis que diga "Usar esta fuente en una pantalla de alta resolución" sería predecible, pero no abordaría el problema principal con imágenes en un diseño adaptable: ahorrar el ancho de banda del usuario. La densidad de píxeles de una pantalla solo está relacionada de forma tangencial con Internet. la velocidad de conexión, si corresponde. Si estás usando una laptop de primera línea, pero estás navegando en la Web mediante una conexión de uso medido, a tu teléfono o si usas una conexión Wi-Fi de avión inestable, es posible que quieras inhabilitar las fuentes de imágenes de alta resolución, la calidad de la pantalla.

Dejar la última palabra sobre el navegador permite obtener muchas más mejoras en el rendimiento que las que podríamos administrar con un enfoque estrictamente prescriptivo. sintaxis. Por ejemplo: en la mayoría de los navegadores, una img con la sintaxis srcset o sizes nunca molestará la solicitud de una fuente con menor más grandes que una que el usuario ya tiene en la caché de su navegador. ¿Cuál sería el sentido de realizar una nueva solicitud relacionada con una fuente? que se vería idéntico, cuando el navegador puede reducir sin problemas la fuente de la imagen que ya tiene? Pero si el usuario escala sus viewport hasta el punto en que se necesita una nueva imagen para evitar el aumento de la escala, aún se hará esa solicitud, por lo que se ve como esperas.

Esa falta de control explícito puede parecer un poco aterrador, pero como usas archivos fuente con contenido, ya no es probable que mostremos a los usuarios un problema "roto" que con un src de una sola fuente, independientemente de las decisiones que toma el navegador.

Usa sizes y srcset

Es mucha información, tanto para ti, el lector como para el navegador. srcset y sizes son sintaxis densas, describiendo una cantidad impactante de información en pocos caracteres. Es decir, para bien o para mal, por diseño: lograr estas sintaxis menos concisas (y más fáciles de analizar por los humanos) podrían haberlas hecho más difíciles de analizar para un navegador. El más complejidad agregada a una cadena, mayor será la posibilidad de que haya errores del analizador o diferencias no intencionales en el comportamiento de un navegador a otro. Sin embargo, hay una desventaja: una sintaxis que las máquinas lee con más facilidad es una sintaxis que se escribe con más facilidad. por ellos.

srcset es un caso claro para la automatización. Es raro que hagas manualmente múltiples versiones de tus imágenes para una de producción, en lugar de automatizar el proceso con un ejecutor de tareas como Gulp, un agrupador como Webpack, una solución CDN, como Cloudinary, o una funcionalidad ya integrada en el CMS que elijas. Si damos suficiente información para generar nuestras fuentes En primer lugar, un sistema tendría suficiente información para escribirlos en un atributo srcset viable.

sizes es un poco más difícil de automatizar. Como sabes, la única forma en que un sistema puede calcular el tamaño de una imagen de una diseño es haber renderizado el diseño. Por suerte, aparecieron varias herramientas para desarrolladores que simplifican el proceso de escritura a mano de los atributos sizes, con una eficiencia que nunca podrías igualar manualmente respImageLint, por ejemplo, es un fragmento de código destinado a aprobar tus atributos sizes. de exactitud y brindar sugerencias para mejorar. Los compromisos del proyecto Lazysizes cierta velocidad para la eficiencia aplazando las solicitudes de imágenes hasta después de que se haya establecido el diseño, lo que permite que JavaScript genera valores sizes por ti. Si usas un framework de renderización completamente del cliente, como React o Vue, hay una Cantidad de soluciones para crear o generar atributos srcset y sizes, que analizaremos con más detalle en CMS y frameworks.