Используйте picture, чтобы переключаться между квадратными и широкими файлами изображений при достижении контрольной ширины области просмотра. Укажите для каждого источника собственные размеры и позвольте CSS пропорционально масштабировать выбранное изображение. В этом руководстве на Mac создаётся и тестируется локальный пример.

Как picture переключается между изображениями с разными соотношениями сторон?

Поместите элемент source с условием media перед резервным img внутри picture. Браузер выбирает первый подходящий источник, условие которого выполнено, а затем выбирает изображение из его srcset. Это называется арт-дирекцией: предоставлением разных композиций для разных вариантов макета. Стандарт HTML прямо поддерживает такой подход. Стандарт HTML: изображения

Совместно работают три компонента: источник выбирает ресурс, атрибуты размеров описывают пропорции этого ресурса, а CSS управляет размером отображаемого изображения. Атрибуты width и height выбранного источника могут задавать соотношение сторон для содержащегося в нём изображения. Сам элемент picture не отображает изображение; это делает его img. Стандарт HTML: встроенное содержимое

Выбирайте механизм в зависимости от нужного результата:

Требование Подходящий механизм
Изменять композицию или соотношение сторон на контрольной ширине picture с элементом source, использующим media
Предлагать разные разрешения одной композиции srcset, а при использовании дескрипторов ширины — sizes
Использовать один файл, обрезая его в фиксированной рамке Настройка размеров средствами CSS и object-fit: cover

Эти механизмы отвечают за разные решения. Арт-дирекция определяет, какая композиция отображается. Выбор разрешения позволяет браузеру подобрать подходящий вариант внутри одной композиции. Подгонка средствами CSS определяет, как выбранный ресурс размещается в своей рамке. В документации MDN описаны эти способы использования picture, его связь с srcset и свойствами подгонки. MDN: элемент picture

Создайте готовый локальный пример на Mac

В примере используются два оригинальных SVG-ресурса с оранжевым кругом и бирюзовым скруглённым прямоугольником. В квадратной версии фигуры расположены одна под другой, а в широкой — рядом. Все файлы находятся в одной папке; для переключения не нужны внешние ресурсы или JavaScript.

Условия для ресурсов просты: при ширине области просмотра меньше 700px используется квадратный ресурс размером 600 × 600. При ширине 700px и больше — широкий ресурс размером 1200 × 600. Отображаемое изображение заполняет область содержимого, оставляя по 16px с каждой стороны, и имеет максимальную ширину 960px. Эти размеры и контрольная ширина выбраны для примера.

Сохраните два ресурса с фигурами

Создайте папку с именем picture-demo. В текстовом редакторе кода создайте в ней два файла, указанных ниже, сохранив их точные имена и расширения. В каждом блоке приведено содержимое файла целиком; скопируйте его без маркеров Markdown-блока.

Сохраните это как shapes-square.svg:

<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600">
  <rect width="600" height="600" fill="#fff5df"/>
  <circle cx="300" cy="180" r="100" fill="#de7b35"/>
  <rect x="150" y="340" width="300" height="160" rx="40" fill="#247d83"/>
</svg>

Сохраните это как shapes-wide.svg:

<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600" viewBox="0 0 1200 600">
  <rect width="1200" height="600" fill="#fff5df"/>
  <circle cx="320" cy="300" r="180" fill="#de7b35"/>
  <rect x="650" y="150" width="400" height="300" rx="60" fill="#247d83"/>
</svg>

Сохраните HTML-страницу

Создайте index.html в той же папке. Если вы используете TextEdit, выберите File > New, а затем Format > Make Plain Text, прежде чем вставлять код. Сохраните файл с расширением .html и при появлении запроса выберите Use .html. Apple описывает этот способ создания HTML-файлов. Apple: Работа с HTML-документами в TextEdit на Mac

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Shape study</title>
  <style>
    body { margin: 0; font-family: system-ui, sans-serif; }
    main { width: calc(100% - 32px); max-width: 960px; margin: 24px auto; }
    picture { display: block; }
    .study-image { display: block; width: 100%; height: auto; }
  </style>
</head>
<body>
  <main>
    <h1>Shape study</h1>
    <picture>
      <source media="(min-width: 700px)" srcset="shapes-wide.svg"
              width="1200" height="600">
      <img class="study-image" src="shapes-square.svg"
           width="600" height="600"
           alt="An orange circle and a teal rounded rectangle on cream.">
    </picture>
    <p>Resize the viewport to compare the two arrangements.</p>
  </main>
</body>
</html>

Откройте index.html в Safari. При достаточно широкой области просмотра фигуры должны располагаться рядом. При более узкой — одна под другой. Храните все три файла вместе: относительные URL ресурсов отсчитываются от расположения HTML-файла.

В широком источнике используется srcset, хотя в нём указан только один URL. Внутри picture спецификация source требует srcset; его атрибут src недопустим. Атрибуты размеров задаются целыми числами без единиц измерения, поэтому используйте width="1200", а не width="1200px". MDN: элемент source

Зачем указывать размеры и для источника, и для резервного изображения?

Размеры резервного изображения 600 × 600 описывают квадратный ресурс. Размеры широкого источника 1200 × 600 описывают широкий ресурс. Если браузер поддерживает такую возможность, обе пары размеров позволяют ему определить пропорции выбранного изображения ещё до его загрузки. Если указать для широкого источника квадратные размеры, начальное соотношение сторон будет неверным. Стандарт HTML: атрибуты размеров источника

Затем CSS задаёт ширину изображения равной доступной ширине области содержимого, оставляя высоту автоматической. Автоматическая высота позволяет определить её исходя из пропорций изображения. В черновике спецификации CSS по настройке размеров описано, как предпочтительное соотношение сторон влияет на автоматическое вычисление размеров; явное задание обеих размеров лишает изображение этой гибкости. Модуль CSS Box Sizing, уровень 4

Для этого примера макет можно проверить с помощью простого расчёта:

ширина изображения = min(ширина области просмотра - 32, 960)
высота квадратного изображения = ширина изображения
высота широкого изображения = ширина изображения / 2
Ширина области просмотра в пикселях CSS Выбранный ресурс Расчётный размер изображения
500 shapes-square.svg 468 × 468
699 shapes-square.svg 667 × 667
700 shapes-wide.svg 668 × 334
1000 shapes-wide.svg 960 × 480

Это расчётные значения для приведённых правил CSS при условии, что полоса прокрутки не уменьшает доступную ширину области содержимого. Синтаксис SVG, имена ресурсов, объявленные размеры и арифметика были проверены программно; при подготовке пример не отображался в браузере.

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

Точно проверьте контрольную ширину в Safari

Режим адаптивного дизайна Safari позволяет менять размер области просмотра страницы независимо от размеров окна на рабочем столе. В настройках Safari Advanced включите Show features for web developers. Откройте пример и выберите Develop > Enter Responsive Design Mode или нажмите Control-Command-R. В документации WebKit описаны настройка, сочетание клавиш и изменение размеров окна предварительного просмотра. WebKit: упрощённый режим адаптивного дизайна

Сначала проверьте ширину 699px и 700px, затем — заметно более узкую и широкую область просмотра. Убедитесь, что композиция меняется на заданной границе, оранжевая фигура остаётся круглой, а абзац под изображением перемещается с учётом изменения его высоты. Перезагрузите страницу по обе стороны контрольной ширины, чтобы проверить и начальный макет, и его изменение при масштабировании.

Чтобы напрямую проверить, какой файл выбрал Safari, выполните этот диагностический фрагмент в консоли Web Inspector, пока открыт пример:

const studyImage = document.querySelector('.study-image');
if (!studyImage) {
  throw new Error('Open the Shape study example before running this snippet.');
}
const studyBox = studyImage.getBoundingClientRect();
console.table({
  selectedURL: studyImage.currentSrc,
  displayedWidth: studyBox.width,
  displayedHeight: studyBox.height
});

Повторно выполните фрагмент после пересечения контрольной ширины. Выбранный URL должен заканчиваться именем соответствующего квадратного или широкого файла. Свойство currentSrc сообщает URL, выбранный браузером; само его значение не доказывает, что ресурс загрузился успешно. MDN: свойство currentSrc объекта HTMLImageElement

Что проверить, если переключение работает неправильно?

Последовательно проверьте выбор ресурса, размеры ресурсов и отображение. Так проблему с размерами CSS будет проще отличить от проблемы с выбором ресурса.

  1. При большой ширине по-прежнему выбирается квадратный файл. Проверьте, что ширина области просмотра достигает 700px, что source стоит перед img, а источник использует srcset. Если источников несколько, проверьте их порядок: более ранний подходящий источник может получить приоритет. Стандарт HTML: выбор арт-дирекции
  2. Ресурс не отображается. Сравните имена файлов в разметке с именами файлов в папке, включая расширения. В этом примере оба SVG-файла должны лежать рядом с index.html.
  3. Правильный ресурс отображается с неправильными пропорциями. Проверьте правила CSS, применённые непосредственно к элементу img, особенно правила, задающие фиксированную высоту. Для пропорционального масштабирования восстановите height: auto.
  4. После загрузки изображение скачет. Сравните каждую объявленную пару размеров с реальными пропорциями соответствующего ресурса. Также проверьте, не переопределяет ли заданное явно значение CSS aspect-ratio нужные пропорции.

Если в вашем дизайне нужна фиксированная рамка, осознанно выберите поведение подгонки. object-fit: contain сохраняет ресурс целиком и может оставлять пустое пространство; cover заполняет рамку и может обрезать содержимое; fill может растягивать изображение. Применяйте свойства подгонки к img. Приведённому примеру фиксированная рамка не нужна: его макет подстраивается под пропорции выбранного ресурса. MDN: object-fit

Каковы ограничения поддержки в браузерах?

Поддержка picture и поддержка размеров, заданных для источников, — это отдельные вопросы. В сводке совместимости Стандарта HTML указано, что picture поддерживается в современных браузерных движках. MDN отмечает, что свойство источника width, отражающее его атрибут размера, доступно во всех браузерах с декабря 2022 года. Не следует рассчитывать, что старые браузеры правильно зарезервируют место с учётом размеров конкретного источника. Стандарт HTML: элемент picture, MDN: свойство width объекта HTMLSourceElement

Если подходящего источника нет или браузер не поддерживает picture, изображение предоставляет резервный img. В этом примере это квадратная композиция. Браузер, который умеет переключать ресурсы, но не поддерживает размеры конкретного источника, может определить итоговые пропорции широкого изображения только после его загрузки. MDN: элемент picture

Если целевой браузер поддерживает CSS aspect-ratio, но для него нужно задать явное правило размеров, добавьте следующий фрагмент CSS в существующую таблицу стилей. Он повторяет контрольную ширину HTML и задаёт предварительные пропорции, позволяя после загрузки использовать естественные пропорции изображения:

.study-image {
  aspect-ratio: auto 1 / 1;
}

@media (min-width: 700px) {
  .study-image {
    aspect-ratio: auto 2 / 1;
  }
}

В MDN описано совместное использование auto и соотношения сторон. Там также указано, что CSS aspect-ratio поддерживается во всех браузерах с сентября 2021 года. Следовательно, у этого фрагмента есть собственные ограничения поддержки. Синхронизируйте его контрольную ширину с условием источника и проверяйте те версии браузеров, которые нужно поддерживать. MDN: aspect-ratio

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

Источники