Плотность пикселей

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

Физические пиксели

Физические пиксели (device pixel или hardware pixel) - самые маленькие элементы любого экрана (его матрицы), каждый из которых имеет свой цвет и яркость.

Разрешение экрана (screen resolution) - это количество физических пикселей на экране устройства. Например, разрешение 1920x1080 означает что экран 1920 физических пикселей по ширине и 1080 по высоте. Если умножить получится общее количество пикселей на таком экране - 2073600.

Плотность экрана (pixel density) - это количество физических пикселей, которое помещается в один дюйм. Измеряется в ppi (pixels per inch). Чем выше плотность, тем меньше размер пикселя и тем больше их на экране.

CSS-пиксели

CSS-пиксели (device-independent pixels) - абстрактная величина не зависящая от экрана устройства и используемая браузером для рассчёта размеров контента на странице.

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

Например, есть блок размером 2x2 пикселя. Все размеры элементов, которые указываются в CSS или HTML, это CSS-пиксели.

div {
  width: 2px;
  height: 2px;
}

На обычных экранах он будет занимать область 2x2 физических пикселя, а на Retina-экранах, например с плотностью 2, этот же блок получит 4x4 физических пикселя. То есть в два раза больше по горизонтали и вертикали. Таким образом, на Retina-экранах с плотностью пикселей в два раза выше стандартной, количество пикселей в 4 раза больше, чем на обычных.

Растровые пиксели

Растровые пиксели (bitmap pixels) - самые маленькие части, составляющие растровое изображение (png, jpg, gif и т. д.). Каждый пиксель содержит информацию о своём цвете и расположении в системе координат изображения.

В коде размеры изображения задаются в CSS-пикселях. При отображении на обычном экране, один растровый пиксель соответствует одному CSS-пикселю. На Retina-экранах с плотностью 2, каждый растровый пиксель умножается в 4 раза, что приводит к потере качества изображения не подготовленного к ретине.

Именно поэтому растровые изображения для Retina-экранов должны иметь большее количество пикселей. Это гарантирует их чёткость при отображении в браузере.

Ретинизация графики

Для Retina-экранов необходимы специально подготовленные ресурсы. Текст и векторная графика (SVG) отображаются одинаково хорошо на любом экране, так как их рендерит сам браузер по набору точек и соединяющих их кривых. Ретинизация - это приём для подготовки растровой графики.

Для того чтобы подготовить растровую графику к ретине, небходимо экспортировать из макета большее изображение. Например, чтобы показать фотографию 200x300 CSS-пикселей на экране с плотностью 2, необходимо подготовить её вариант в размере 400x600 растровых пикселей. Для экрана с плотностью 3 это изображение должно быть 600x900 растровых пикселей.

info Для растровой веб-графики достаточно подготовить только 1x и 2x версии. Большинство людей с хорошим зрением не заметят разницу между 2x и 3x изображением, из-за физических ограничений человеческого глаза, но вес последнего будет значительно больше, что негативно скажется на скорости его загрузки.

Процесс подготовки состоит из экспорта изображений в N-раз больше размера оригинала и сохранения их с соответствующими префиксами @2x и @3x. Для оригинала префикс не нужен.

После чего достаточно задать нужный размер тегу <img /> в HTML или CSS коде.

В примере используется онлайн-сервис изображений. Загружается три картинки 320x240 (1x), 640x480 (2x) и 960x720 (3x) пикселей. Размер всех элементов <img /> одинаковый - 320x240 CSS-пикселей. Если перейти в пример с телефона или просто увеличить масштаб страницы с примером, будет заметно что первое изображения сильно размывается, второе остаётся вполне качественным, а третье всегда кристально чёткое.

info Это не значит что всегда нужно экспортировать и загружать самое большое изображение. На этом шаге мы рассмотрели проблему и научились делать ретинизацию растровой графики. Дальше мы научимся загружать различные изображения под каждую ширину или плотность экрана.

Отзывчивые изображения

Представьте изображение 1200x600 пикселей. Оно отлично выглядит на широком экране. А что будет, если мы откроем страницу на телефоне или планшете? Ничего хорошего. Изображение будет оставаться шириной 1200 пикселей и появится горизонтальная полоса прокрутки, а на ретине оно будет выглядеть размыто.

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

Самый простой способ реализовать отзывчивость, это использовать одно изображение для всех устройств, задав ему определённый набор CSS-свойств.

/* Свойства нужно применить ко всем изображениям,
поэтому используется селектор тега. */
img {
  display: block;
  max-width: 100%;
  height: auto;
}

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

Создадим контейнер для картинки div.thumb, зададим ему минимальную и максимальную ширину. Картинке дадим отзывчивые свойства. Теперь картинка будет всегда красиво заполнять «тумбу», изменяя свой размер в зависимости от ширины родителя.

A random image

Этот базовый подход для контентных изображений слишком простой и не решает проблему Retina-экранов и веса большого изображения на мобильных устройствах, но это хороший старт.

Отзывчивые фоновые изображения

Для ретинизации фонового изображения необходимо задать размер фона элемента равным размеру самого элемента.

.box {
  width: 200px;
  height: 300px;
  background-image: url('photo@2x.png');
  background-size: 200px 300px;
}

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

.box {
  background-image: url('photo@2x.png');
  background-size: cover;
}

При таком подходе браузер загрузит 2x изображение на любом экране - не идеально. Поэтому существуют медиа-функции позволяющие определить плотность пикселей экрана в CSS. Внутри набора таких медиа-функций, переопределяем путь к фоновому изображению.

/* Базовые стили и 1x изображение */
.box {
  width: 480px;
  height: 320px;
  background-image: url('photo.png');
  background-size: 480px 320px;
}

/* Переопределяем путь к 2x изображению
если плотность экрана как минимум 2 */
@media (min-device-pixel-ratio: 2),
  (min-resolution: 192dpi),
  (min-resolution: 2dppx) {
.box {
  background-image: url('photo@2x.png');
  }
}

В медиа-функции min-device-pixel-ratio указывается численное значение пиксельной плотности экрана - коэфициент между физическими и CSS-пикселями. Также необходимо указать функцию min-resolution с двумя разными значениями.

Медиа-функция min-device-pixel-ratio поддерживается некоторыми браузерами только с вендорным префиксом, поэтому если обработать этот код автопрефиксером получим финальный вариант.

.box {
  width: 480px;
  height: 330px;
  background-image: url('photo.png');
  background-size: 480px 320px;
}

@media (min-device-pixel-ratio: 2),
(-webkit-min-device-pixel-ratio: 2),
(min-resolution: 192dpi),
(min-resolution: 2dppx) {
.box {
  background-image: url('photo@2x.png');
  }
}

infoТакая солянка из медиа-функций и их значений нужна потому, что они еще не стандартизированы между разными браузерами.

В примере задано одинаковое фоновое изображение в различных размерах для экранов со стандартной (1x) и удвоенной (2x) плотностью пикселей.

Если открыть инструменты разработчика и перейти на вкладку Network, то будет видно, что загружается только одно изображение - подходящее под плотность экрана на котром открыта веб-страница. При входе с Macbook Pro 15" 2017, плотность пикселей экрана которого равна 2, браузер загрузит изображение в два раза больше стандартного.

Отзывчивый элемент <img>

Отзывчивый элемент <img> используется браузером для загрузки только подходящего под текущее устройство изображения из предложенных разработчиком. Разработчик указывает список доступных к загрузке изображений, а браузер, основываясь на размере экрана устройства и его пиксельной плотности, выбирает самое подходящее.

1

Например, разработчик указал два доступных изображения - 300×300 и 600×600 пикселей. Если браузеру достаточно картинки 300×300, это четырёхкратная экономия веса загружаемого изображения. Чем меньше размер экрана устройства, тем больше выгода от такого подхода. Экономия трафика на мобильном устройстве может достигать 70-90% по сравнению с десктопом.

Атрибут srcset

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

<img srcset="" src="" alt="" />

Дескриптор X

Самый простой способ использования отзывчивого элемента <img> это применение дескриптора x, который буквально указывает браузеру для какой плотности экрана подходит эта версия изображения.

<img
  srcset="photo.jpg 1x, photo@2x.jpg 2x"
  src="photo.jpg"
  alt="Описание изображения для всех версий"
/>

В атрибуте src указывается версия изображения в стандартном качестве, которая будет использована в случае если браузер старый и не значет о srcset. В атрибуте srcset указываем 1x и 2x версии изображения, разделяя объявления запятой. Если страница будет открыта на устройстве с плотностью пикселей 2 или выше, вместо стандартного будет использовано изображение photo@2x.jpg.

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

Если открыть инструменты разработчика и перейти на вкладку Network, то будет видно, что загружается только одно изображение - подходящее под плотность экрана на котром открыта веб-страница. При входе с Macbook Pro 15" 2017, плотность пикселей экрана которого равна 2, браузер загрузит изображение у которого указан дескриптор 2x.

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

Задать жёсткие размеры изображения на экране не всегда возможно, потому что при увеличении вьюпорта необходимо будет показывать изображение 300x200 в блоке размерами 600x400 пикселей или больше.

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

Дескриптор w

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

<img
  srcset="photo-300.jpg 300w, photo-600.jpg 600w, photo-1200.jpg   1200w"
  src="photo-300.jpg"
  alt="Описание изображения для всех версий"
/>

Помечая каждое изображение дескриптором w, в котором указана ширина этого изображения в пикселях, мы перекладываем все вычисления и выбор изображения на браузер.

infoЕсли изображение photo-600.jpg размером 600x400 пикселей, то помечаем его дескриптором 600w. В имени файла изображения указываем его физическую ширину, чтобы не забыть, так как версий изображения может быть много.

Атрибут sizes

При использовании дескриптора w, то есть при привязке к физическим рамзерам изображения, необходимо использовать атрибут sizes, чтобы подсказать браузеру приблизительный размер холста на котором в браузере будет отрисовываться изображение. Размеры можно указывать как в пикселях, так и в относительных единицах.

Например, если изображение на экране всегда будет 300px, достаточно указать одно значение.

<img
  srcset="photo-300.jpg 300w, photo-600.jpg 600w,   photo-1200.jpg 1200w"
  sizes="300px"
  src="photo-300.jpg"
  alt="Описание изображения для всех версий"
/>

В случае когда изображение должно быть на 100% ширины вьюпорта на мобильных устройствах до 600px, 300px на экранах 601-900px и 600px на экранах шире, значение sizes будет включать в себя медиа-запросы.

<img
  srcset="photo-300.jpg 300w, photo-600.jpg 600w, photo-1200.jpg 1200w"
  sizes="(min-width: 900px) 600px, (min-width: 600px) 300px, 100vw"
  src="photo-300.jpg"
  alt="Описание изображения для всех версий"
/>

infoБраузер читает медиа-выражения указанные в атрибуте sizes слева направо и выбирает первое подходящее, поэтому надо внимательно следить за порядком их объявления.

Как браузер выбирает

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

На илюстрации показан десктоп и смартфон с шириной изображения 100% ширины вьюпорта. Напишем разметку отзывчивого элемента <img /> для этого случая.

<img
  srcset="img-1200x714.png 1200w, img-3000x1768.png 3000w"
  sizes="100vw"
  src="img.png"
  alt="Описание изображения для всех версий"
/>

Браузер умножает ширину вьюпорта на плотность пикселей.

В случае когда в sizes задано фиксированное значение ширины изображения, браузер умножит его на плотность пикселей. Например, sizes задано значение 400px.

<img
  srcset="
  photo-300.jpg 300w,
  photo-600.jpg 600w,
  photo-900.jpg 900w,
  photo-1200.jpg 1200w
  "
  sizes="400px"
  src="photo-300.jpg"
  alt="Описание изображения для всех версий"
/>

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

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

Элемент <picture>

Как мы уже разобрались, отзывчивый элемент <img> позволяет загружать разные версии (размеры) одного и того же изображения. Элемент <picture> используется в случае когда необходимо загрузить изображения разного формата (расширения), изображения различные по геометрии (кадрирование) или изображения для поддержки режима тёмной темы..

infoВ каждой ситуации необходимо использовать правильный инструмент, в большинстве случаев будет достаточно отзывчивого элемента <img>, не обязательно везде применять <picture>.

Синтаксис

Элемент <picture> это контейнер для одного или более элементов <source>, в которых перечисляются возможные варианты, и одного обязательного элемента <img>.

<picture>
  <source srcset="" media="" sizes="" type="" />
  < source srcset="" media="" sizes="" type="" />
  <img src="" alt="" />
</picture>

Поддерджка современных форматов

Элемент <picture> применяется для загрузки изображений в современных форматах (например webp). Для браузеров которые не поддерживают такие форматы, указывается дефолтное изображение в альтернативном формате (например jpg или png). Атрибут type указывает MIME-тип изображения. Если браузер не подерживает его, то этот элемент <source> пропускается.

Следующий пример определяет элемент <picture>, который позволит браузерам загрузить photo.webp, при этом предоставляется альтернатива photo.jpg для браузеров которые еще не поддерживают webp.

<picture>
  <source srcset="photo.webp 1x, photo@2x.webp 2x" type="image/webp" />
  <source srcset="photo.jpg 1x, photo@2x.jpg 2x" type="image/jpeg" />
  <img src="photo.jpg" alt="Кот" />
</picture>

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

infoДля того чтобы быстро и удобно сконвертировать изображения в формат webp, можно использовать онлайн-инструменты, например squoosh.app.

Кадрирование (art direction)

Техника кадрирования используется для загрузки различных изображений для каждого экрана. Это позволяет показать пользователю максимум полезной информации и деталей изображения даже на маленьких устройствах. Например, загружать альбомную (landscape) или книжную (portrait) версию изображения в зависимости от ориентации или разрешения устройства.

Атрибут media позволяет определить медиа-запрос, который браузер будет анализировать для выбора элемента <source>. Если медиа-запрос определяется как ложный (не подходит), то этот элемент <source> пропускается.

<!-- Для экранов шире 600px будет загружен photo-wide.jpg -->
<picture>
<source srcset="photo-wide.jpg" media="(min-width: 600px)" />
<img src="photo.jpg" alt="Фотография" />
</picture>

Lore ipsum dolor sit amet.

Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas repellat rerum numquam labore quos, obcaecati ad id saepe nam eius.

Для поддержики экранов с высокой плотностью пикселей, каждому элементу <source> необходимо добавить набор изображений с дескрипторами в атрибут srcset, и не забыть задать атрибут sizes.

Добавим в код предыдущего примера поддержку ретины с плотностью 2x используя дескриптор w. Ограничим ландшафтную версию изображения максимальной шириной 800px, если вьюпорт шире чем 800 пикселей. В противном случае изображение тянется на 100% вьюпорта.

Lorem ipsu dolor sit amet.

Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas repellat rerum numquam asd quos, obcati ad id saepe nam eius.