company_banner

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

Автор оригинала: Scott Vandehey
  • Перевод
Спецификация по отзывчивым изображениям — это фантастический документ, в котором описано множество вариантов использования таких изображений. Но опыт подсказывает мне, что чаще всего при работе с ними нужно знать лишь о том, как отдавать клиенту копии одного и того же изображения разного размера, выбирая их в зависимости от ширины области просмотра страницы. Мы называем это «переключением разрешения». Для решения этой задачи можно воспользоваться атрибутами srcset и sizes.

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



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

Как тег «общается» с браузером


Вот как тег <img> сообщает браузеру о том, какое изображение нужно выбрать.


«Разговор» тега и браузера

Здесь тег <img> сообщает браузеру следующее: «При таком размере экрана я буду выводиться в примерно следующем размере (указывает на атрибут sizes). Можешь взять любое из этих изображений, которые имеют следующую ширину (указывает на атрибут srcset). Поэтому, пожалуйста, выбери то изображение, которое сейчас подходит лучше всего».

Атрибут srcset


Атрибут srcset даёт браузеру набор графических ресурсов, из которых он может выбирать самый подходящий. Здесь же находятся сведения о размере каждого из предлагаемых изображений.


Атрибут srcset

В атрибуте src содержится резервный вариант изображения, предназначенный для браузеров, не понимающих атрибута srcset. В srcset содержатся URL изображений и сведения об их ширине. Браузер выберет из предоставленного ему набора изображений то, что подходит лучше всего. Причём, если у пользователя имеется дисплей с повышенной плотностью пикселей (Retina-дисплей) — браузер это учтёт при выборе изображения.

Атрибут srcset содержит список URL изображений, за которыми следуют сведения о ширине изображений. Пары URL-ширина разделены запятыми. Элементы списка выглядят примерно так: image.jpg 1000w. Такая запись сообщает браузеру о том, что изображение image.jpg имеет 1000 пикселей в ширину.

Описывая набор изображений таким способом, мы сообщаем браузеру следующее: «Я даю тебе список изображений и доверяю тебе выбор самого подходящего».

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

Браузер достаточно интеллектуален для того чтобы знать, что на настольном дисплее низкого разрешения, в том случае, если ширина выводимого изображения составляет 800 пикселей, нужно выбрать из списка то изображение, ширина которого составляет, как минимум, 800 пикселей.

Браузер, кроме того, знает о том, что если изображение шириной 320 пикселей выводится на дисплее высокого разрешения, нужно выбрать файл изображения, которое имеет в ширину, как минимум, 640 пикселей. В результате нам не нужно беспокоиться об 1x- и 2x-графических ресурсах. Всё, что нужно сделать — это дать браузеру хороший набор изображений и позволить ему делать своё дело.

Как описывать изображения, подходящие для разных экранов?

Атрибут sizes


Атрибут srcset — это прекрасный инструмент. Но когда браузер читает HTML-код страницы, он не знает о том, используется ли, например, CSS-стиль, который устанавливает размер изображения в 50% от ширины экрана. 

Именно тут в игру вступает атрибут sizes. С его помощью мы можем дать браузеру подсказку о том, в каком размере будет выводиться изображение после применения к нему CSS.


Атрибут sizes

  • 100vw — размер, используемый по умолчанию в том случае, когда ни одно из условий не выполняется. Он указывается последним в списке, который имеется в атрибуте sizes.
  • 1023px — ширина окна.
  • 780px — ширина изображения при выполнении указанного условия.

Браузер остановится на первом выполненном условии.

В атрибуте sizes содержится список медиа-условий, разделённых запятыми. Медиа-условия — это подмножество медиа-запросов. Тут нельзя указывать тип среды, к которой применимо условие (print или screen), но можно использовать медиа-запросы, относящиеся к ширине области просмотра.

Каждая запись списка содержит ширину окна области просмотра и соответствующую ей ширину изображения. Элемент списка имеет вид (min-width: 1023px) 780px. Подобная запись сообщает браузеру о том, что если ширина области просмотра составляет 1023 пикселя (или больше), то нужно использовать изображение шириной 780 пикселей.

Здесь, кроме того, можно использовать относительные единицы измерения ширины. Например — что-то вроде 50vw. Это сообщает браузеру о том, что ширина изображения будет составлять 50% ширины области просмотра. Тут, в более сложных ситуациях, можно даже использовать функцию calc. Например, конструкция вида calc(50vw — 2rem) сообщает браузеру о том, что ширина изображения будет составлять 50% ширины области просмотра за вычетом 2rem. Возможно, ширина задана именно так для учёта ширины какого-нибудь отступа или какой-нибудь границы.

Последний элемент списка не снабжён медиа-условием. Если внести в список ширину и не указать медиа-условие, соответствующее значение будет рассматриваться как значение, используемое по умолчанию, то есть — в том случае, если ни одно из других условий не выполняется.

Браузер будет просматривать этот список сверху вниз и остановится на первом подходящем элементе, соответствующем ширине области просмотра.

Предположим, что в атрибуте sizes находится следующее:

(min-width: 1023px) 780px,
(min-width: 675px) 620px,
100vw

Вот что будет происходить в разных ситуациях:

  • Если пользователь работает на большом настольном дисплее, то браузер сочтёт подходящим первый элемент списка и будет знать о том, что ширина изображения должна составлять 780 пикселей.
  • Стандартная ширина области просмотра вертикально расположенного экрана iPad составляет 768 пикселей. В такой ситуации браузер пропустит первую запись списка, но обнаружит совпадение текущих условий со второй записью. Она сообщает браузеру о том, что ширина изображения будет составлять 620 пикселей.
  • Если пользователь смотрит страницу на типичном мобильном устройстве, то браузер не сочтёт подходящими две первых записи в sizes. Поэтому он дойдёт до последней записи, которая скажет ему о том, что ширина изображения будет составлять 100% ширины области просмотра.

Конечно, это — лишь примеры. В реальном мире всё куда сложнее. Например, пользователь с большим экраном, просматривающий страницу в узком окне браузера, получит изображение меньшего размера, чем получил бы, будь окно развёрнуто на весь экран. Пользователь с iPad Pro вполне может получить более крупное изображение в том случае, если расположил устройство в ландшафтном режиме. Если он пользуется планшетом в портретном режиме, ему может достаться изображение среднего размера. А если он пользуется браузером в режиме, когда экран делится между несколькими окнами, то ему может достаться маленькое изображение. Некоторые телефоны с большими экранами будут соответствовать второму правилу из нашего списка при работе с ними в ландшафтном режиме. В этом и состоит прелесть описываемой системы: разработчику не нужно заботиться обо всех этих особых случаях и о разных форм-факторах устройств. Ему достаточно сообщить браузеру о том, какой вариант изображения нужно выбрать для конкретной области просмотра.

Как готовить список изображений, из которых браузер будет выбирать наиболее подходящее?

Атрибут src


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

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

Вопросы и ответы


▍Как генерировать наборы изображений?


Изображения можно генерировать разными способами: вручную, с использованием инструмента для создания отзывчивых изображений, с применением соответствующих возможностей CDN.

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

Это может потребовать достаточно много времени, поэтому у вас может возникнуть желание автоматизировать эту работу, воспользовавшись соответствующим инструментом. Среди подобных инструментов мне больше всего нравится Responsive Image Breakpoints Generator от Cloudinary. При работе с этим инструментом достаточно передать ему изображение, после чего он автоматически создаст его варианты разных размеров. Тут можно настроить количество генерируемых изображений. После того, как изображения готовы, их можно загрузить и использовать в проекте.

Ещё один вариант поддержки отзывчивых изображений заключается в том, чтобы использовать для их хостинга соответствующую CDN. Например — Cloudinary или imgix. Используя подобный сервис, на CDN загружают изображение в наивысшем имеющемся разрешении. Затем можно запрашивать версии изображения разных размеров, пользуясь URL-параметрами. При этом вам не придётся заботиться об изменении размеров изображения: достаточно сообщить CDN о том, в каком размере вы собираетесь выводить соответствующее изображение.

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

▍Изображения каких размеров следует предоставлять браузеру?


Хороший вопрос! Если вы дадите браузеру слишком много графических ресурсов, то вы попросту впустую потратите время и силы на их создание. Если же ресурсов будет слишком мало — это будет означать, что вы принуждаете пользователей вашего сайта к загрузке изображений, размеры которых больше, чем им нужно.

Если вы работаете с единственным изображением и у вас есть возможность самостоятельно настроить разметку для вывода этого изображения, то можете воспользоваться инструментом Responsive Image Breakpoints Generator. Он автоматически исследует изображение и примет решение о том, каким является оптимальный набор ресурсов (в плане баланса между размерами файлов и их разрешением), сгенерированный на основе этого изображения. Затем этот инструмент не только сгенерирует файлы, но и автоматически подготовит атрибуты srcset и sizes.


Стандартный набор размеров изображений шириной от 320 до 2560 пикселей

Если вы работаете в некоей CMS, или если создаёте веб-приложение, и при этом не знаете о том, изображение какого размера будет выведено в том или ином месте, тогда я рекомендую воспользоваться стандартным набором размеров изображений. Раньше я использовал следующие размеры: 320w, 640w, 960w, 1280w, 1920w и 2560w. Это — круглые цифры, получаемые путём умножения 320 на разные коэффициенты. Этот набор ресурсов покрывает нужды самых разных экранов — от маленьких мобильных дисплеев, до огромных настольных мониторов.

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

Если вы хостите изображения на Cloudinary, то вам будет доступен ещё один подход. Он заключается в использовании API Cloudinary, который позволяет обрабатывать изображения с помощью Responsive Image Breakpoints Generator при их загрузке. После того, как изображения автоматически обработаны, можно, воспользовавшись ответом API, динамически заполнить атрибуты srcset и sizes.

▍Какие значения следует вносить в атрибут sizes?


Для того чтобы выяснить то, какие значения следует вносить в атрибут sizes, нужно проанализировать CSS и понять, изображения какой ширины выводятся в различных условиях.

Иногда это определяется шириной самого изображения:

img {
  width 320px;
}

@media screen and (min-width: 37.5em) {
  width: 640px;
}

В данном случае имеются два фиксированных варианта размера изображения. Этот факт может найти прямое отражение в атрибуте sizes:

<img
  alt="Ferrari"
  src="ferrari.jpg"
  srcset="ferrari-s.jpg 320w,
          ferrari-m.jpg 960w,
          ferrari-l.jpg 1920w"
  sizes="(min-width: 37.5em) 640px, 320px">

Однако часто оказывается так, что размеры изображений настраиваются гибко. Нередко изображения наследуют размеры от своих контейнеров:

img {
  height: auto;
  width: 100%;
}

.container {
  padding: 1rem;
  width: 100%;
}

@media screen and (min-width: 37.5em) {
  .container {
    width: 50%;
  }
}

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

<img
  alt="Ferrari"
  src="ferrari.jpg"
  srcset="ferrari-s.jpg 320w,
          ferrari-m.jpg 960w,
          ferrari-l.jpg 1920w"
  sizes="(min-width: 37.5em) calc(50vw - 2rem),
         calc(100vw - 2rem)">

Как видите, настройка атрибута sizes сильно зависит от особенностей конкретного макета. Обычно я строю работу, начиная с исследования изображений в инструментах разработчика браузера и выясняя то, какие параметры влияют на размер изображений.

▍Как проверить правильность настройки отзывчивых изображений?


Легко представить себе то, что тестирование правильности настройки отзывчивых изображений может быть сложным и длительным делом. Но, к нашему счастью, существует инструмент, упрощающий решение подобных задач. Это — Responsive Image Linter.

Это — букмарклет, который можно добавить в браузер и использовать на собственном сайте. Когда его вызывают, он автоматически сканирует страницу, используя для тестирования изображений различные размеры области просмотра и плотность пикселей экрана.

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

Итоги


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

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

Уважаемые читатели! Пользуетесь ли вы отзывчивыми изображениями в своих проектах?

RUVDS.com
RUVDS – хостинг VDS/VPS серверов

Комментарии 16

    0
    Как раз сейчас занимаюсь этим вопросом в своем проекте.
    Однако, пока не придумал как быть, когда человек получил маленькое изображение на мобильный согласно srcset, но все же хочет рассмотреть его получше?
    Что обычно делают в таких случаях? Разворачивать popup окно с первоначальным большим изображением по клику на изображении в статье? Но клик такое ненадежное дело — может человек просто прокрутить статью хотел…
      0
      Отличайте клик от драга. Хотя бы с помощью таймаута между mousedown(touchstart) и moseup(touchend).
        +2
        Зачем, если onсlick на тач-скринах и так интерпретируется как тап и не срабатывает при драге.
          0
          Я перечитал множество статей про srcset и все они примерно одинаковы, как близнецы братья, как вот эта статья. Но нигде не нашел простого доступного приемлемого решения конкретно вот этой проблемы — что делать, когда пользователь все же хочет рассмотреть картинку.
          Сам я такое наверное долго буду писать, ибо не программист javascript ни разу.
          Проект у меня крошечный, самопальный, при том на joomla, что сейчас уже считается наверное моветоном. Но проекту больше 10 лет и там сотни статей и тысячи картинок. Бросать жалко.
          Для джумлы нашел плагин github.com/ttc-freebies/plugin-responsive-images который в статьи будет автоматически вставлять эти srcset. При этом сам плагин может создавать на лету из имеющихся статей по тегам масштабированные картинки в кеше по разным размерам. Но это может замедлить сайт.
          Поэтому решил написать скрипт, который заранее создаст мне все нужные размеры:
          #!/bin/bash
          srcdir="/var/www/site/public_html/images"
          dstdir="images"
          
          function scandir
          {
          mkdir -p "${dstdir}/$1"
          for i in $srcdir/$1/*
              do
          	if [ -d "${i}" ] ; then
          	    dir=$(basename "${i}")
          	    echo "-------------------------"
          	    echo "dir:  $srcdir/$1/$dir"
          	    scandir "$1/$dir"
          	else
          	    filename_full=$(basename -- "${i}")
          	    ext="${filename_full##*.}"
          	    filename="${filename_full%.*}"
          	    echo "file: ${filename}.${ext}"
          	    if [ "$ext" = jpg ] || [ "$ext" = JPG ] || [ "$ext" = png ] || [ "$ext" = PNG ]; then
          		width=$(identify -format "%w" ${i})
          		if (($width > 1200))
          		then
          		    echo "1200"
          		    convert "${i}" -quality 85 -resize 1200x1200 "${dstdir}/$1/${filename}_1200.${ext}"
          		fi
          		if (($width > 992))
          		then
          		    echo "992"
          		    convert "${i}" -quality 85 -resize 992x992 "${dstdir}/$1/${filename}_992.${ext}"
          		fi
          		if (($width > 762))
          		then
          		    echo "762"
          		    convert "${i}" -quality 85 -resize 762x762 "${dstdir}/$1/${filename}_762.${ext}"
          		fi
          		if (($width >640))
          		then
          		    echo "640"
          		    convert "${i}" -quality 85 -resize 640x640 "${dstdir}/$1/${filename}_640.${ext}"
          		fi
          		if (($width >480))
          		then
          		    echo "480"
          		    convert "${i}" -quality 85 -resize 480x480 "${dstdir}/$1/${filename}_480.${ext}"
          		fi
          		if (($width >320))
          		then
          		    echo "320"
          		    convert "${i}" -quality 85 -resize 320x320 "${dstdir}/$1/${filename}_320.${ext}"
          		fi
          	    fi
          	fi
              done
          }
          
          scandir "stories"
          


          Примерно так (но еще не до конца потестировал скрипт)…
          Не знаю что из этого получится, но буду пробовать…
            0

            Есть npm пакет для этого. Более того. Найдите крупные проекты где применяют srcset. Я только medium встречал.

        0
        Интересно б посмотреть на тот сайт, который может себе позволить так себе заморачиватся по поводу изображений. *Могу представить только сайт по продаже Феррари. А так всё конечно правильно написано, но что б такое поддерживать в реальном проекте на нормальном качестве надо отдельного сотрудника нанимать
          0
          Если сайт приносит прибыль, то это называется оптимизация сайта с целью повышения прибыли через лояльность клиентов. Возможно текст поста кажется сложным. Можете посмотреть моё обучающее видео на эту тему, где я показываю сочетание использования CDN imgix с IntersectionObserver. Выглядит довольно просто. Вот ссылка на момент про изображения youtu.be/EzO0hQxczjQ?t=1658
            0
            Pimcore делает почти всё автоматом )
              0
              Инстаграм. Фейсбук.
              Ну все крупные сайты, где есть много фото материала. Обычному сайту визитке проще использовать сжатые маленькие изображения с подгрузкой больших в скрытую, если надо.
              0
              Когда же этот min-width закончится уже?
              В нормальных браузерах поддерживается ResizeObserver (а для остальных есть iframe-хак). С его помощью легко написать свой вывод адаптивных картинок, который будет основываться на размерах доступного контейнера, а не window.
              Есть и готовое из коробки, например, github.com/marcj/css-element-queries.
                +1

                Херово работает этот квери элемент. Во-первых, дофига заметно как он работает. Во-вторых есть моменты когда он входит в цикл, т.е его новое состояние тригерит само себя. И поэтому получается адский стробоскоп. Поэтому менять размер шрифта им часто стремно

                0
                Браузер выберет наилучшее изображение, руководствуясь сложным набором критериев, в которые входит то, изображение какого размера выводится у пользователя, то, каков текущий размер области просмотра, и то, имеется ли у пользователя дисплей высокого разрешения.

                Когда-то ещё упоминали, что браузер может также учитывать качество интернет соединения. Не проверяли такие случаи?
                Условно, даже если у клиента ретина дисплей, но медленный интернет — скачается картинка с меньшим разрешением, а не с 2x.
                  0

                  Проверяли, увы, с медленным интернетом тянется тоже 2х, но ужимается в режиме Экономии трафика

                  0

                  Ребят, ну вы чего? В 2020 без этого уже никак, стандарт
                  Шаг 1. Добавляем 2-6 точек под сетку
                  Шаг 2. Добавляем 1.5х-2х для устройств с большой плотностью пикселей
                  Шаг 3. Не забываем про .webp и loading="lazy"
                  Для любых сайтов, делает верстальщик, статику ручками, динамику отдает на бек

                    0
                    что это за 1 шаг такой с точками?
                      0
                      Возмём 3 произвольные точки:

                      <picture>
                          <source srcset="изображение_для_экранов_меньше_360" media="(max-width: 360px)">
                          <source srcset="изображение_для_экранов_меньше_700" media="(max-width: 700px)">
                          <source srcset="изображение_для_экранов_меньше_1240" media="(max-width: 1240px)">
                          <img src="оригинальное_изображение">
                      </picture>
                      

                      Сначала проверяется первый source, если экран устройства меньше либо равен 360px, то используется это изображение, если больше, переходит к следующему source.
                      Если ни один из source не подошел по условию, используется оригинальное изображение

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

                  Самое читаемое