Руководство по синтаксису адаптивных изображений в HTML - Web-Global: Связывая миры через веб-технологии

Руководство по синтаксису адаптивных изображений в HTML

Пост опубликован 10 ноября 2022 в 09:36 и находится в рубриках HTML&CSS, Лайф хаки. 142
Поделиться:

Это руководство о синтаксисе HTML для адаптивных изображений (и немного CSS для хорошей оценки). Синтаксис адаптивных изображений предназначен для использования одного изображения из нескольких вариантов на основе правил и обстоятельств. Существует две формы адаптивных изображений, и они предназначены для двух разных целей:

Если ваша единственная цель…

Повышенная производительность

Тогда вам нужно…

<img
  srcset=""
  src=""
  alt=""
>

Использование адаптивных изображений значительно повышает производительность. Вес изображения оказывает огромное влияние на общую производительность страниц, и адаптивные изображения — одна из лучших вещей, которые вы можете сделать, чтобы уменьшить вес изображения. Представьте, что браузер может выбирать между изображением размером 300 × 300 или 600 × 600. Если браузеру нужно всего 300 × 300, это потенциально экономия в 4 раза больше байт!Экономия обычно увеличивается по мере уменьшения разрешения дисплея и размера области просмотра; на самых маленьких экранах несколько тематических исследований показали экономию байтов в 70-90%.

Если вам также нужно…

Управление дизайном

Тогда вам нужно…

<picture>
  <source srcset="" media="">
  <source srcset="" media="">
  <img src="" alt="">
</picture>

Еще одна вполне законная цель адаптивных изображений — не просто использовать разные размеры одного и того же изображения, но и использовать разные изображения. Например, обрезка изображения по-разному в зависимости от размера экрана и различий в макете. Это называется “художественным направлением”.

<picture>Элемент также используется для резервных типов изображений и любого другого переключения медиа-запросов (например, разные изображения для темного режима). Вы получаете больший контроль над тем, что отображают браузеры.

Использование srcset

<img srcset="" src="" alt="">Синтаксис предназначен для отображения версий одного и того же изображения разного размера. Вы могли бы попытаться использовать совершенно разные изображения, используя этот синтаксис, но браузеры предполагают, что все в a srcsetвизуально идентично, и будут выбирать тот размер, который они считают лучшим, способами, которые невозможно предсказать. Поэтому я бы не рекомендовал это.

Возможно, самый простой из возможных синтаксисов адаптивных изображений — это добавление srcsetатрибута с xдескрипторами к изображениям, чтобы пометить их для использования на дисплеях с разной плотностью пикселей.

<img 
  alt="A baby smiling with a yellow headband."
  src="baby-lowres.jpg"
  srcset="baby-highres.jpg 2x"
>

Здесь мы сделали по умолчанию (src) копию изображения с низким разрешением (1 ×). Обычно разумным выбором является использование по умолчанию самых маленьких / быстрых ресурсов. Мы также предоставляем версию 2 ×. Если браузер знает, что он отображается с более высокой плотностью пикселей (2xдеталь), он будет использовать это изображение вместо.

ДЕМОНСТРАЦИЯ
<img 
  alt="A baby smiling with a yellow headband."
  src="baby-lowres.jpg"
  srcset="
    baby-high-1.jpg 1.5x,
    baby-high-2.jpg 2x,
    baby-high-3.jpg 3x,
    baby-high-4.jpg 4x,
    baby-high-5.jpg 100x
  "
>

Вы можете создавать столько вариантов плотности пикселей, сколько захотите.

Хотя это круто и полезно, на xдескрипторы приходится лишь небольшой процент использования адаптивных изображений. Почему? Они позволяют браузерам адаптироваться только на основе одной вещи: отображать плотность пикселей. Однако часто наши адаптивные изображения находятся в адаптивных макетах, и размер макета изображения уменьшается и растягивается вместе с областью просмотра. В таких ситуациях браузеру необходимо принимать решения, основываясь на двух вещах: плотности пикселей экрана и размере макета изображения. Вот где wпоявляются дескрипторы и sizesатрибут, которые мы рассмотрим в следующем разделе.

Использование srcsetwsizes

Это хороший материал. На это приходится около 85% использования адаптивных изображений в Интернете.Мы по-прежнему показываем одно и то же изображение разных размеров, только мы предоставляем браузеру больше информации, чтобы он мог адаптироваться на основе плотности пикселей и размера макета.

<img 
  alt="A baby smiling with a yellow headband."
  srcset="
    baby-s.jpg  300w,
    baby-m.jpg  600w,
    baby-l.jpg  1200w,
    baby-xl.jpg 2000w
  "
  sizes="70vmin"
>

Мы по-прежнему предоставляем несколько копий одного и того же изображения и позволяем браузеру выбирать наиболее подходящее. Но вместо того, чтобы помечать их плотностью пикселей (x), мы помечаем их шириной ресурса, используя wдескрипторы. Итак, если baby-s.jpgэто 300 × 450, мы обозначаем это как 300w.

Использование srcsetс дескрипторами width (w), подобными этому, означает, что его нужно будет связать с sizesатрибутом, чтобы браузер знал, в каком большом пространстве будет отображаться изображение. Без этой информации браузеры не смогут делать разумный выбор.

ДЕМОНСТРАЦИЯ

Создание точных sizes

Создание sizesатрибутов может быть сложным. sizesАтрибут описывает ширину, которую изображение будет отображать в макете вашего конкретного сайта, что означает, что оно тесно связано с вашим CSS. Ширина, с которой отображаются изображения, зависит от макета, а не только от области просмотра!

Давайте посмотрим на довольно простой макет с тремя точками останова. Вот видео, демонстрирующее это:ДЕМОНСТРАЦИЯ

Точки останова выражаются с помощью медиа-запросов в CSS:

body {
  margin: 2rem;
  font: 500 125% system-ui, sans-serif;
}
.page-wrap {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr 200px;
  grid-template-areas:
    "header header"
    "main aside"
    "footer footer";
}

@media (max-width: 700px) {
  .page-wrap {
    grid-template-columns: 100%;
    grid-template-areas:
      "header"
      "main"
      "aside"
      "footer";
  }
}
@media (max-width: 500px) {
  body {
    margin: 0;
  }
}

Размер изображения различается в каждой точке останова. Вот разбивка всех фрагментов, которые влияют на ширину макета изображения в наибольшей точке останова (когда окно просмотра шире 700 пикселей):

Изображение имеет такую же ширину, как 100vwминус все явные размерыmarginpadding, ширина столбцов и gap.
  • При самом большом размере: существует явный интервал 9rem, поэтому изображение calc(100vw - 9rem - 200px)широкое. Если бы в этом столбце frвместо unit использовался unit 200px, мы бы как бы облажались здесь.
  • При среднем размере: боковая панель опускается ниже, поэтому нужно учитывать меньше интервалов. Тем не менее, мы можем сделатьcalc(100vw - 6rem), чтобы учесть поля и отступы.
  • При наименьшем размере: поле тела удаляется, так что just calc(100vw - 2rem)сделает свое дело.

Фу!Честно говоря, я обнаружил, что это немного сложно продумать, и допустил кучу ошибок, когда создавал это. В конце концов, у меня было это:

<img 
  ...  
  sizes="
    (max-width: 500px) calc(100vw - 2rem), 
    (max-width: 700px) calc(100vw - 6rem),
    calc(100vw - 9rem - 200px)
  "
/>

sizesАтрибут, который дает браузеру ширину изображения по всем трем точкам останова, учитывая сетку макета и все окружающиеgapmargin, и paddingэто в конечном итоге влияет на ширину изображения.

Теперь подождите! Барабанная дробь! 🥁🥁🥁 Это все равно неправильно. Я не понимаю, почему именно, потому что для меня это выглядит так, как будто это на 100% описывает то, что происходит в макете CSS. Но это неправильно, потому что так говорит RespImageLint Мартина Аусвегера. Запуск этого инструмента поверх изолированной демонстрации не вызывает проблем, за исключением того факта, что sizesатрибут неверен для некоторых размеров области просмотра и должен быть:

<img
  ...
  sizes="
    (min-width: 2420px) 2000px, 
    (min-width: 720px) calc(94.76vw - 274px), 
    (min-width: 520px) calc(100vw - 96px), 
    calc(100vw - 32px)
  "
>

Я не знаю, как это вычисляется, и это совершенно невозможно поддерживать вручную, но это точно. Инструмент Мартина программно изменяет размер страницы и записывает sizesатрибут, который описывает фактическую, наблюдаемую ширину изображения в широком диапазоне размеров области просмотра. Это компьютеры, которые занимаются математикой, так что это правильно. Итак, если вам нужен сверхточный sizesатрибут, я бы рекомендовал сначала просто установить неправильный, запустить этот инструмент и скопировать правильный.

Для еще более глубокого погружения во все это ознакомьтесь с wописаниями Эрика Портиса иsizes : Под капотом .

Быть более спокойным sizes

Другой вариант — использовать метод Horseshoes & Hand Grenades ™ of sizes(или, другими словами, close counts). Это настоятельно рекомендуется.

Например, sizes="96vw"говорится: “Это изображение будет довольно большим на странице — почти во всю ширину — но по краям всегда будет немного отступов, так что не совсем. Или sizes="(min-width: 1000px) 33vw, 96vw"говорит: “Это изображение в формате трех столбцов на больших экранах и близко к полноразмерному в противном случае”. С практической точки зрения это может быть разумным решением.

Вы можете обнаружить, что некоторые автоматические адаптивные графические решения, которые не могут знать ваш макет, делают предположение — что-то вроде sizes="(max-width: 1000px) 100vw, 1000px". Это просто говорит: “Эй, мы на самом деле мало знаем об этом макете, но мы собираемся попробовать и сказать, что в худшем случае изображение будет полноразмерным, и будем надеяться, что оно никогда не будет отображаться больше 1000 пикселей”.

Абстрагирование sizes

Я уверен, вы можете себе представить, как легко не только sizesошибиться, но и со временем это станет неправильным, поскольку макеты меняются на вашем сайте. Возможно, вам будет разумно абстрагировать его с помощью языка шаблонов или фильтра содержимого, чтобы вы могли легче изменять значение для всех ваших изображений.

По сути, я говорю о sizesтом, чтобы один раз задать значение в переменной и использовать эту переменную в нескольких разных <img>элементах вашего сайта. Родной HTML не предлагает этого, но любой внутренний язык предлагает; например, константы PHPпеременные конфигурации Rails, контекстный API React, используемый для глобальной переменной состояния, или переменные в языке шаблонов, например Liquid, могут использоваться для абстракцииsizes.

<?php
  // Somewhere global
  $my_sizes = "";
?>

<img
  srcset=""
  src=""
  alt=""
  sizes="<?php echo $my_sizes; ?>"
/>

“Выбор браузера”

Теперь, когда у нас есть sizesатрибут, браузер знает, какой размер (или близкий к нему) будет отображать изображение, и может творить свое волшебство. То есть он может выполнить некоторую математику, которая учитывает плотность пикселей экрана и размер, при котором будет отображаться изображение, а затем выбрать изображение наиболее подходящего размера.

Математика на первый взгляд довольно проста. Допустим, вы собираетесь показать изображение шириной 40 Вт в окне просмотра шириной 1200 пикселей на экране с плотностью пикселей 2x. Идеальное изображение должно иметь ширину 960 пикселей, поэтому браузер будет искать самое близкое, что у него есть. Браузер всегда будет вычислять целевой размер, который он предпочел бы, исходя из ситуации с областью просмотра и плотностью пикселей, а также из того, что он знаетsizes, и сравнивать эту цель с тем, что он должен выбратьsrcset. Однако то, как браузеры выполняют выбор, может быть немного странным.

Браузер может учитывать больше вещей в этом уравнении, если захочет. Например, он может учитывать текущую скорость сети пользователя или то, включил ли пользователь какое-либо предпочтение “экономии данных”. Я не уверен, что какие-либо браузеры действительно делают подобные вещи, но они могут это делать, если захотят, поскольку именно так была написана спецификация. Некоторые браузеры иногда предпочитают извлекать данные из кэша. Если математика показывает, что они должны использовать изображение размером 300 пикселей, но у них уже есть 600 пикселей в локальном кэше, они просто будут использовать это. Умный.Пространство для такого рода вещей является сильной сторонойsrcset/sizes синтаксис. Именно поэтому вы всегда используете разные размеры одного и того же изображения внутриsrcset: у вас нет способа узнать, какое изображение будет выбрано. Это выбор браузера.

Это странно. Разве браузер уже не знает об этом?

Вы можете подумать: “Хм, почему я должен указывать браузеру, какого размера будет отображаться изображение, разве он этого не знает?”Ну, это так, но только после того, как он загрузил ваш HTML и CSS и выложил все. sizesАтрибут касается скорости. Это дает браузеру достаточно информации, чтобы сделать разумный выбор, как только он увидит ваш<img> .

<img
  data-sizes="auto"
  data-srcset="
    responsive-image1.jpg 300w,
    responsive-image2.jpg 600w,
    responsive-image3.jpg 900w"
  class="lazyload" 
/>

еперь вы можете подумать: “А как насчет изображений с отложенной загрузкой?”(например, к тому времени, когда запрашивается изображение с отложенной загрузкой, макет уже выполнен, и браузер уже знает размер рендеринга изображения). Что ж, хорошая мысль! Библиотека Александра Фаркаса lazysizes sizesавтоматически записывает атрибуты в lazyload, и в настоящее время ведется дискуссия о том, как сделать авто-sizes для изображений, загруженных с задержкой, изначально.

sizes может быть больше, чем область просмотра

Краткое примечаниеsizes. Допустим, у вас есть эффект на вашем сайте, чтобы изображение “увеличивалось” при нажатии на него. Может быть, он расширяется, чтобы заполнить весь видовой экран, или, может быть, он еще больше увеличивает масштаб, чтобы вы могли видеть больше деталей. В прошлом нам, возможно, приходилось менять кнопку srcon click, чтобы переключиться на версию с более высоким разрешением. Но теперь, предполагая, что источник с более высоким разрешением уже находится вsrcset, вы можете просто изменить sizesатрибут на что-то огромное, например200vw, или 300vw, и браузер должен автоматически загрузить источник с более высоким разрешением для вас. Вот статья Скотта Джела об этой технике.

Как насчет адаптивных изображений в CSS с фоновыми изображениями?

Мы рассмотрели именно это раньше.Хитрость заключается в использовании @mediaзапросов для изменения background-imageисточника. Например:

.img {
  background-image: url(small.jpg);
}
@media 
  (min-width: 468px),
  (-webkit-min-device-pixel-ratio: 2), 
  (min-resolution: 192dpi) {
  .img {
    background-image: url(large.jpg);
  }
}

С помощью этого синтаксиса CSS, в зависимости от условий браузера, браузер будет загружать только одно из двух изображений, что обеспечивает ту же цель производительности, что и синтаксис адаптивных изображений в HTML. Если это поможет, подумайте о приведенном выше как о CSS-эквиваленте <picture>синтаксиса: браузер должен следовать вашим правилам и отображать то, что соответствует.

Если вы хотите, чтобы браузер выбирал лучший вариант, например srcset/sizes, но в CSS, решением в конечном итоге будет image-set()функцияimage-set()Однако сегодня есть две проблемы:

  • Поддержки для этого пока нет. Реализация Safari лидирует в пакете, но image-set()уже восемь лет имеет префикс в Chrome, а в Firefox его вообще нет.
  • Даже сама спецификация кажется отсталой от времени. Например, он поддерживает только xдескрипторы (wпока нет).

Лучше всего пока просто использовать медиа-запросы.

Вам нужно полизаполнять?

Я довольно уверен в том, что заполню все это прямо сейчас. Тем не менее, существует отличное полизаполнение, называемое Picturefill, которое обеспечит вам полную поддержку IE 9-11, если вам это нужно. Помните, однако, что ничто из этого не приводит к тому, что в неподдерживаемых браузерах вообще не отображается какое-либо изображение, предполагая, что у вас <img src="" alt="">где-то есть. Если вы сделаете (довольно безопасное) предположение, что IE 11 работает на настольном дисплее с низкой плотностью пикселей, вы можете заставить свои источники изображений отражать это по умолчанию и строить оттуда.

Другие важные соображения по изображению

  • Оптимизация качества: смысл адаптивных изображений заключается в загрузке наименьшего, наиболее эффективного ресурса, который вы можете. Вы не сможете достичь этого без эффективного сжатия вашего изображения. Вы стремитесь к тому, чтобы каждое изображение выглядело хорошо и было легким. Мне нравится позволять сервисам хостинга изображений решать эту проблему за меня, но у Etsy есть действительно отличный обзор того, чего они смогли достичь с помощью инфраструктуры, которую они создали сами.
  • Обслуживание с CDN: Говоря об услугах хостинга изображений, скорость проявляется во многих формах. Быстрые серверы, географически близкие к пользователю, также являются важным фактором скорости.
  • Кэширование: что может быть лучше, чем загрузка меньшего количества данных по сети? Загрузка данных вообще отсутствует! Для этого и существует HTTP-кэширование. Используя Cache-Controlзаголовок, вы можете указать браузеру сохранять изображения, чтобы, если одно и то же изображение понадобится снова, браузеру не нужно было переходить по сети, чтобы получить его, что значительно повышает производительность при повторных просмотрах.
  • Отложенная загрузка: это еще один способ полностью избежать загрузки изображений. Отложенная загрузка означает ожидание загрузки изображения до тех пор, пока оно не окажется в области просмотра или рядом с ней. Так, например, изображение, расположенное далеко внизу страницы, не будет загружаться, если пользователь никогда не прокручивает его.

Поддержка браузера

Это для srcset/sizes, но это то же самое для <picture>.

Эти данные поддержки браузера взяты из Caniuse, в котором содержится более подробная информация. Число указывает, что браузер поддерживает эту функцию в этой версии и выше.