Это руководство о синтаксисе 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
атрибут, которые мы рассмотрим в следующем разделе.
srcset
/ w
+ sizes
Это хороший материал. На это приходится около 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 пикселей):
calc(100vw - 9rem - 200px)
широкое. Если бы в этом столбце fr
вместо unit использовался unit 200px
, мы бы как бы облажались здесь.calc(100vw - 6rem)
, чтобы учесть поля и отступы.calc(100vw - 2rem)
сделает свое дело.Фу!Честно говоря, я обнаружил, что это немного сложно продумать, и допустил кучу ошибок, когда создавал это. В конце концов, у меня было это:
<img
...
sizes="
(max-width: 500px) calc(100vw - 2rem),
(max-width: 700px) calc(100vw - 6rem),
calc(100vw - 9rem - 200px)
"
/>
sizes
Атрибут, который дает браузеру ширину изображения по всем трем точкам останова, учитывая сетку макета и все окружающиеgap
, margin
, и 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
. Допустим, у вас есть эффект на вашем сайте, чтобы изображение “увеличивалось” при нажатии на него. Может быть, он расширяется, чтобы заполнить весь видовой экран, или, может быть, он еще больше увеличивает масштаб, чтобы вы могли видеть больше деталей. В прошлом нам, возможно, приходилось менять кнопку src
on click, чтобы переключиться на версию с более высоким разрешением. Но теперь, предполагая, что источник с более высоким разрешением уже находится вsrcset
, вы можете просто изменить sizes
атрибут на что-то огромное, например200vw
, или 300vw
, и браузер должен автоматически загрузить источник с более высоким разрешением для вас. Вот статья Скотта Джела об этой технике.
Мы рассмотрели именно это раньше.Хитрость заключается в использовании @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()
Однако сегодня есть две проблемы:
image-set()
уже восемь лет имеет префикс в Chrome, а в Firefox его вообще нет.x
дескрипторы (w
пока нет).Лучше всего пока просто использовать медиа-запросы.
Я довольно уверен в том, что заполню все это прямо сейчас. Тем не менее, существует отличное полизаполнение, называемое Picturefill, которое обеспечит вам полную поддержку IE 9-11, если вам это нужно. Помните, однако, что ничто из этого не приводит к тому, что в неподдерживаемых браузерах вообще не отображается какое-либо изображение, предполагая, что у вас <img src="" alt="">
где-то есть. Если вы сделаете (довольно безопасное) предположение, что IE 11 работает на настольном дисплее с низкой плотностью пикселей, вы можете заставить свои источники изображений отражать это по умолчанию и строить оттуда.
Cache-Control
заголовок, вы можете указать браузеру сохранять изображения, чтобы, если одно и то же изображение понадобится снова, браузеру не нужно было переходить по сети, чтобы получить его, что значительно повышает производительность при повторных просмотрах.Это для srcset
/sizes
, но это то же самое для <picture>
.
Эти данные поддержки браузера взяты из Caniuse, в котором содержится более подробная информация. Число указывает, что браузер поддерживает эту функцию в этой версии и выше.