Теги img и picture предназначены для загрузки изображений. Каждый из них позволяет задать набор правил, согласно которым браузер будет выбирать, какое из изображений загружать. Рассмотрим синтаксис и различия данных тегов. Для начала нужно задать следующий метатег:

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

Данный метатег указывает браузеру выполнять масштабирование размеров экрана устройства. Так, например, размеры экрана iPhone X составляют 375x812 css-пикселей.

Для тестирования будем использовать следующее изображение:

Медведи
Медведи

Тег img

Атрибут srcset предназначен для указания всех доступных размеров изображений и URL каждого из них. При этом, тег src указывать нужно. Его значение будет использовано, если все варианты из srcset не подойдут согласно указанным правилам.

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

  1. URL изображения.

  2. Дескриптора ширины.

Рассмотрим на примере:

<img
  src="bears-1920x1080.jpg"
  alt="bears"
  srcset="
    bears-480x270.jpg    480w,
    bears-960x540.jpg    960w,
    bears-1920x1080.jpg 1920w
  "
/>

Ширина всех доступных изображений указывается в пикселях. По историческим причинам для обозначения пикселей используется символ w. В данном примере атрибут srcset содержит три изображения, ширина которых 480, 960 и 1920 пикселей соответственно. Браузер выберет изображение в зависимости от ширины экрана устройтва и его плотности пикселей. Если ширина экрана не превышает 480 css-пикселей, то будет выбрано следующее изображение:

  1. bears-480x270.jpg, если коэфициент плотности пикселей равен 1.

  2. bears-960x540.jpg, если коэфициент плотности пикселей равен 2 (retina display).

  3. bears-1920x1080.jpg, если коэфициент плотности пикселей больше 2.

Аналогично, если ширина экрана больше 480 css-пикселей, но не превышает 960 css-пикселей, то:

  1. bears-960x540.jpg, если коэфициент плотности пикселей равен 1.

  2. bears-1920x1080.jpg, если коэфициент плотности пикселей больше или равен 2.

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

  1. Медиа выражения.

  2. Ширины изображения.

Рассмотрим на примере:

<img
  src="bears-1920x1080.jpg"
  alt="bears"
  sizes="(max-width: 600px) 480px, (max-width: 1200px) 960px, 100vw"
  srcset="
    bears-480x270.jpg    480w,
    bears-960x540.jpg    960w,
    bears-1920x1080.jpg 1920w
  "
/>

В данном примере браузер будет следовать следующим правилам при выборе источника изображения:

  1. Если ширина экрана устройства составляет не более 600 css-пикселей, то изображение на таком экране занимает максимум 480 css-пикселей в ширину.

  2. Если ширина экрана устройства составляет от 600 до 1200 css-пикселей, то изображение на таком экране занимает максимум 960 css-пикселей в ширину.

  3. В противном случае браузер будет подразумевать, что изображение может занимает всю ширину экрана.

Также нужно учитывать коэфициент плотности пикселей. Например, если ширина экрана устройства составляет 550 css-пикселей, то браузер выберет следующее изображение:

  1. bears-480x270.jpg, если коэфициент плотности пикселей равен 1. Так как ширина экрана устройства не превышает 600 css-пикселей, то изображение на таком экране занимает максимум 480 css-пикселей в ширину.

  2. bears-960x540.jpg, если коэфициент плотности пикселей равен 2.

  3. bears-1920x1080.jpg, если коэфициент плотности пикселей больше 2.

Атрибут sizes нужно указывать, если размеры изображения ограничены css стилями. Если атрибут sizes не указан, то по умолчанию он будет иметь значение 100vw (sizes="100vw"), то есть браузер будет подразумевать, что изображение может занимает всю ширину экрана.

Тег picture

Тег picture служит контейнером для одного или более тегов source и одного тега img. Тег source представляет собой источник изображения. Он содержит информацию о формате изображения и его размерах, а также правила, при соблюдении которых браузер должен выбрать этот источник. Если все источники не подходят, то будет выбран файл, указанный в атрибуте src тега img. Если сразу несколько источников подходят, то браузер выберет первый по порядку.

Тег source имеет атрибуты sizes и srcset. Они работают также, как и соотвествующие атрибуты у тега img. Рассмотрим на примере:

<picture>
  <source
    srcset="bears-480x270.jpg 480w, bears-960x540.jpg 960w, bears-1920x1080.jpg 1920w"
    sizes="(max-width: 600px) 480px, (max-width: 1200px) 960px, 100vw"
  />
  <img src="bears-1920x1080.jpg" alt="bears" />
</picture>

Данный пример работает так же, как и второй пример использования тега img.

Разница между img и picture

Тег picture позволяет указать браузеру использовать разные изображения в зависимости от размера экрана. Достигается это за счёт использования атрибута media тега source, который позволяет задать медиа выражение, при котором будет использоваться данный источник. Например, на маленьких экранах мы хотим использовать обрезанное изображение cropped-bears.jpg, которое содержит основную часть изображения:

Медведи
Медведи

Для этого нужно указать несколько тегов source и задать им атрибуты media:

<picture>
  <source
    media="(max-width: 480px)"
    srcset="
      cropped-bears-480x270.jpg 480w,
      cropped-bears-960x540.jpg 960w
    "
  />
  <source
    media="(max-width: 960px)"
    srcset="bears-960x540.jpg 960w, bears-1920x1080.jpg 1920w"
  />
  <img src="bears-1920x1080.jpg" alt="bears" />
</picture>

В данном примере, если ширина экрана устройства не превышает 480 css-пикселей, то будет выбрано обрезанное изображение. Добиться такого результата при помощи тега img не получиться, так как изображения cropped-bears-960x540.jpg и bears-960x540.jpg имеют одинаковый размер, но изображение cropped-bears-960x540.jpg предназначено для использования на устройстве, ширина которого не превышает 480 css-пикселей и коэфициент протности пикселей равен 2, а изображение bears-960x540.jpg - на устройстве, ширина которого от 480 до 960 css-пикселей и коэфициент протности пикселей равен 1.

Также, тег picture позволяет указать различные форматы изображения, например webp и jpeg. Для этого нужно тегу source задать атрибут type:

<picture>
  <source
    type="image/webp"
    media="(max-width: 480px)"
    srcset="
      cropped-bears-480x270.webp 480w,
      cropped-bears-960x540.webp 960w
    "
  />
  <source
    type="image/webp"
    media="(max-width: 960px)"
    srcset="bears-960x540.webp 960w, bears-1920x1080.webp 1920w"
  />
  <source
    type="image/jpeg"
    media="(max-width: 480px)"
    srcset="
      cropped-bears-480x270.jpg 480w,
      cropped-bears-960x540.jpg 960w
    "
  />
  <source
    type="image/jpeg"
    media="(max-width: 960px)"
    srcset="bears-960x540.jpg 960w, bears-1920x1080.jpg 1920w"
  />
  <img src="bears-1920x1080.jpg" alt="bears" />
</picture>

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

Комментарии (11)


  1. mistergrim
    11.08.2021 20:02

    Как уничтожить веб, серия №-я.


  1. ivan386
    11.08.2021 20:27
    +1

    Я поэкспериментировал в Firefox и получил следующие результаты:

    1. При отсутствии sizes браузер выбирает первое изображение которое шире окна.

    2. При наличии sizes браузер выбирает изображение шире заданного размера.

    <img
      src="bears-1920x1080.jpg"
      alt="bears"
      sizes="(max-width: 600px) 480px, (max-width: 1200px) 960px, 100vw"
      srcset="
        bears-480x270.jpg    480w,
        bears-960x540.jpg    960w,
        bears-1920x1080.jpg 1920w
      "
    />

    Тобиж по крайней мере в Firefox картинка bears-480x270.jpg в этом примере не будет показана никогда.


    1. nightzen Автор
      12.08.2021 21:18
      +2

      Протестировал в Firefox 91.0 (Mozilla Firefox for Ubuntu). Работает также, как описано в статье.


      1. ivan386
        13.08.2021 00:08
        +1

        Да косяк у меня. DPR был больше 1.


        DPR = 1:


        DPR = 1
        Всё правильно.


        DPR = 2:


        image
        Выбирается изображение больше.


        Тестовая страница


  1. Daniel217D
    15.08.2021 21:58

    Следовало указать, что это перевод статьи с английского - оригинал


    1. nightzen Автор
      15.08.2021 21:59

      Это исходная статья, просто она находилась 5 дней на модерации.


  1. Makito
    27.08.2021 14:27

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

    Хотя в спецификации о таком поведении нигде не написано.


    1. ivan386
      28.08.2021 03:39

      В каком браузере такое поведение? В Firefox 91.0.2 у меня при сужении окна браузер выбирает картинку меньше.


      1. Makito
        28.08.2021 10:04

        Хром 92. А вы после сужения снова расширьте, пройдитесь несколько раз по всем брекпойнтам без перезагрузки страницы, и посмотрите какой размер картинки берется для рендера.


        1. ivan386
          28.08.2021 16:18

          Вот я выше делал тестовую страницу. Сколько я бы её не менял по ширине туда и обратно Firefox меняет изображения согласно заданному sizes.


          1. Makito
            28.08.2021 21:01

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

            В общем с хромом что то неладно.