Использование изображений в Интернете стало возможным в 1995 году благодаря внедрению тега <img>
в спецификацию HTML 2.0.
Простой синтаксис для выполнения простой работы.
Но во времена современного Интернета и увеличения количества устройств авторам HTML нужен был способ отображать разные изображения в зависимости от размера экрана. Поскольку изображения сильно влияют на производительность, мы не хотим загружать большое изображение на мобильный телефон.
Мы используем адаптивные изображения для показа различных изображений в зависимости от устройства, на котором они просматриваются. Мы загрузим изображение меньшего размера для экранов меньшего размера, что уменьшит размер сетевого запроса.
Для создания адаптивных изображений Консорциум Всемирной паутины (W3C) предлагает два решения:
- Использование тега
srcset
вimg
теге: простое решение, которое охватит большинство случаев - Использование
picture
элемента с исходными текстами внутри: решение, обеспечивающее больший контроль над вашими изображениями, позволяющее нам давать более точные инструкции браузеру
- Адаптивные изображения с использованиемsrcset
- Базовый пример: таргетинг на плотность пикселей
- Лучше: использовать дескриптор ширины
- Еще лучше: добавляемразмерыатрибут
- Полностью управляйте изображением с помощьюкартинка
- Базовый пример: Разные изображения
- Поддержка современного формата изображений
- Использованиеsrcsetс синтаксисомpicture
- Автоматизируйте создание адаптивных изображений
- Адаптивные изображения «На лету» с использованием сети доставки контента (CDN)
- Адаптивные изображения на WordPress
- Создание их на вашем сервере
- Адаптивные изображения в CSS
- Использование медиа-запросов
- Использованиенабор изображений
- Адаптивное видео
Адаптивные изображения с использованием srcset
Использование img
с srcset
используется для отображения одного и того же изображения в разных размерах.
Если мы хотим отобразить другое изображение (например, увеличенную версию для мобильных устройств или версию в темном режиме), мы захотим использовать picture
вместо этого.
Действительно, при использовании srcset
мы устанавливаем рекомендации для нашего браузера, но мы не можем предсказать, какое изображение браузер на самом деле предпочтет. Браузер может отображать увеличенную версию, поскольку она уже есть в его кэше.
Базовый пример: таргетинг на плотность пикселей
<img src="goat.jpg" srcset="goat.jpg, goat-2x.jpg 2x, goat-4x.jpg 4x" alt="A goat climbing a mountain" />
Здесь мы загрузили в наш браузер три разных изображения, используя дескриптор плотности (например, 2x), который будет отображать изображение, соответствующее плотности экрана устройства.
Лучше: использовать дескриптор ширины
<img src="goat.jpg" srcset="goat.jpg 800w, goat-1200.jpg 1200w, goat-1800.jpg 1800w" alt="A goat climbing a mountain" />
Здесь браузер возьмет ширину области просмотра, умножит ее на плотность пикселей и выберет изображение ближайшего размера.
Например, на устройстве с шириной области просмотра 400px
и плотностью 3
, браузер будет искать изображение с шириной, ближайшей к 3 * 400px = 1200px
– в данном случае, goat-1200.jpg
.
Вы будете использовать этот синтаксис большую часть времени, поскольку он одновременно обрабатывает ширину устройства и его плотность.
Хотя этот синтаксис полезен для многих вариантов использования, он не зависит от макета, что означает, что он может быть не оптимизирован для изображений, которые не занимают 100% ширины устройства.
Еще лучше: добавляем размеры атрибут
Обычно изображение занимает только часть ширины страницы, а не всю ее целиком. Для этого мы можем использовать атрибут sizes
, чтобы определить размер изображения относительно области просмотра.
Пример того, как ширина изображения может варьироваться в зависимости от макета:
Мы можем добавлять несколько значений и использовать условия мультимедиа следующим образом:
<img src="goat.jpg" srcset="goat.jpg 800w, goat-1200.jpg 1200w, goat-1800.jpg 1800w" sizes="(max-width: 500px) 100vw, (max-width: 700px) 96vw, 60vw" alt="A goat climbing a mountain" />
Здесь у нас есть три размера, определенные в нашем атрибуте sizes:
(max-width: 500px) 100vw
: Если размер области просмотра меньше 500 пикселей, изображение будет занимать 100% ширины области просмотра(max-width: 700px) 96vw
: Если размер области просмотра меньше 700 пикселей, изображение будет занимать 96% ширины области просмотра60vw
: В противном случае изображение будет занимать 60% ширины области просмотра
Полностью управляйте изображением с помощью картинка
Использования srcset
и sizes
достаточно для большинства случаев, но браузер по-прежнему применяет свой «секретный соус» в отношении того, какое изображение он будет загружать. В некоторых случаях может отображаться увеличенная версия изображения, поскольку оно уже было в кэше.
В большинстве случаев это нормально, но иногда у нас будут совершенно разные изображения разных размеров (например, обрезанная картинка для мобильных устройств), и использование picture
гарантирует, что браузер загрузит ожидаемое изображение – точно в точке останова, которую мы определили.
Мы также используем picture
для указания резервного формата изображения, поэтому мы можем использовать совершенно новый формат изображения без потери совместимости с устаревшими браузерами.
Базовый пример: Разные изображения
Если мы используем не только одни и те же изображения разного размера, но и разные изображения, мы называем это художественным направлением.
picture
Элемент содержит по крайней мере один img
и может иметь несколько источников на выбор, в зависимости от отображения.
Именно так мы объявляем эти разные изображения, используя picture
:
<picture>
<source
srcset="illustration-big.png"
media="(min-width: 1200px)"
/>
<source
srcset="illustration-medium.png"
media="(min-width: 800px)"
/>
<img
src="illustration-small.png"
alt="An admin panel"
/>
</picture>
Мы можем указать условия использования носителя, используя media
атрибут, в большинстве случаев мы проверяем ширину.
Поддержка современного формата изображений
Мы также можем использовать picture
синтаксис для элегантной поддержки современных форматов изображений. Если браузер его не поддерживает, он вернется к img
элементу. Это работает даже со старым Internet Explorer (IE)!
Мы используем type
атрибут, внутри которого мы определяем тип носителя, используя формат Управления по присвоению номеров в Интернете (IANA):
<picture>
<source srcset="illustration-small.webp" type="image/webp" />
<img src="illustration-small.png" type="image/png" alt="An admin panel" />
</picture>
Использование srcset с синтаксисом picture
Мы также можем определить srcset
внутри picture
синтаксиса.
Но помните, что srcset
это всего лишь рекомендации, поэтому мы уверены в источнике, но не в srcset
. Браузер сам решит, что загружать.
<picture>
<source
srcset="
landscape-800 800w,
landscape-1600 1600w"
type="image/png"
media="(min-width: 700px)"
/>
<source
srcset="
portrait-200 200w,
portrait-400 400w,
portrait-600 600w"
type="image/png"
/>
<img src="<https://via.placeholder.com/400x200.png?text=Fallback>"/>
</picture>
В этом примере у нас есть несколько размеров для альбомной и портретной версий.
Мы полностью контролируем, какая версия будет отображаться, и позволяем браузеру загружать для нее нужный размер.
Автоматизируйте создание адаптивных изображений
Загрузка различных изображений на основе области просмотра — это здорово, но нам не нужно создавать эти варианты вручную.
Нам не нужно экспортировать каждое изображение в пяти разных размерах.
Давайте рассмотрим, как мы можем создавать их автоматически.
Адаптивные изображения «На лету» с использованием сети доставки контента (CDN)
Многие CDN/хостинговые службы предлагают способ изменять размер ваших изображений, указывая размер в URL, вот некоторые из них, которые предоставляют эту функцию:
Это очень полезно, и поскольку мы обычно переносим наши изображения в CDNS для повышения производительности, это может стать решением для вас.
Адаптивные изображения на WordPress
Начиная с версии 4.4, любое изображение, которое вы загружаете в свою медиатеку, будет сгенерировано в пяти различных размерах.
При использовании изображения в публикации оно будет адаптивным по умолчанию. Хороший пример прозрачного, но эффективного способа работы с адаптивными изображениями.
Создание их на вашем сервере
Это решение я использую в своем личном блоге, и оно отлично работает.
Я просто загружаю свои изображения, а image-responsiver позаботится обо всем остальном.
Адаптивные изображения в CSS
Использование медиа-запросов
Загрузка нужного изображения в CSS — это просто вопрос использования медиа-запроса (@media
):
.hero {
background-image: url(background-800.jpg);
}
@media
(min-width: 800px){
.hero{
background-image: url(background-1400.jpg);
}
}
Теперь браузер будет загружать только нужное изображение. В нашем примере мы используем min-width
, но мы могли бы также настроить плотность экрана:
.hero {
background-image: url(background-1x.jpg);
}
@media (min-resolution: 2dppx),
(-webkit-min-device-pixel-ratio: 2){
.hero{
background-image: url(background-2x.jpg);
}
}
Использование набор изображений
Также существует новый API, image-set
который предназначен для обработки адаптивных изображений в CSS, как мы это делаем с srcset
.
Синтаксис выглядит следующим образом:
.hero {
background-image: url("background-1x.jpg"); /* fallback */
background-image: image-set(
url("background-1x.jpg") 1x,
url("background-2x.jpg") 2x);
}
Оно имеет ту же структуру, что и атрибут srcset
.
Мы также можем использовать этот синтаксис для обслуживания различных форматов изображений:
.hero {
background-image: url("background.jpg"); /* fallback */
background-image: image-set(
url("background.avif") type("image/avif"),
url("background.jpg") type("image/jpeg"));
}
Адаптивное видео
Когда имеешь дело с видео, которое должно быть не основным контентом, а частью дизайна, загрузка адаптированной версии видео имеет решающее значение для производительности. Загрузка видео в формате 4k на мобильный телефон была бы огромной тратой вычислительных ресурсов.
В идеале у нас должен быть синтаксис, подобный picture
, например, этому:
<video class="responsive-video" preload autoplay loop preload="none" muted>
<source src="header-360p.webm" type="video/webm" media="(min-width: 641px)"></source>
<source src="header-480p.webm" type="video/webm" media="(min-width: 855px)"></source>
<source src="header-720p.webm" type="video/webm" media="(min-width: 1281px)"></source>
<source src="header-1080p.webm" type="video/webm" media="(min-width: 1921px)"></source>
<source src="header-4k.webm" type="video/webm"></source>
</video>
Но, к сожалению, это, скорее всего, никогда не будет реализовано в браузерах.
Что мы можем сделать, так это использовать некоторый JavaScript для загрузки нужного видео в зависимости от размера экрана. Основным недостатком является то, что это решение работает только с min-width
и не может, например, настроить плотность экрана.
Теперь наш синтаксис будет выглядеть следующим образом:
<video class="responsive-video" preload autoplay loop preload="none" muted>
<source src="header-360p.webm" type="video/webm"></source>
<pseudo-source src="header-360p.webm" type="video/webm" max-size="641"></pseudo-source>
<pseudo-source src="header-480p.webm" type="video/webm" max-size="855"></pseudo-source>
<pseudo-source src="header-720p.webm" type="video/webm" max-size="1281"></pseudo-source>
<pseudo-source src="header-1080p.webm" type="video/webm" max-size="1921"></pseudo-source>
<pseudo-source src="header-4k.webm" type="video/webm"></pseudo-source>
</video>
Обратите внимание, что мы используем pseudo-source
сейчас, чтобы это не мешало работе браузера. Кроме того, мы используем не media
больше, а max-size
атрибут.
source
По умолчанию используется уменьшенная версия, и JavaScript заменит ее исходный код на соответствующий.
Давайте добавим этот фрагмент JavaScript в наш код:
const handleSourceChange = () => {
// get the video element
const video = document.querySelector(".responsive-video");
if (!video) return;
// get all the sources
const sources = video.querySelectorAll("pseudo-source");
// the source that we'll replace with the matching video
const actualSource = video.querySelector("source");
// getting the viewport width
const width = video.clientWidth;
// sort the sources
const sortedSources = elementsToArray(sources).sort(
(a, b) => a.maxWidth - (b? b.maxWidth: -1)
);
// we try the max widths and keep the last that matched
let lastSource;
sortedSources.forEach((item, key) => {
if (width > item.maxWidth) {
lastSource = sortedSources[key+1];
}
});
// if none worked, we'll load the last one
if (!lastSource) lastSource = sortedSources[0];
// change the source of the actual element
if (lastSource.src!= actualSource.getAttribute("src")) {
video.pause();
let elapsed = video.currentTime;
actualSource.setAttribute("src", lastSource.src);
video.load();
video.currentTime = elapsed;
video.play();
}
};
// utility function
const elementsToArray = (elements) => {
const results = [];
elements.forEach((item) => {
results.push({
src: item.getAttribute("src"),
maxWidth: item.getAttribute("max-size"),
});
});
return results;
};
// handling responsive videos on load
document.addEventListener("DOMContentLoaded", handleSourceChange);
И это все!