Проблема с отображением картинок возникла с момента появления адаптивности в интернете. Мы хотим, чтобы сайт хорошо смотрелся на любом планшете, телефоне, в портретной или ландшафтной ориентации экрана, а также на супербольших дисплеях 5K. Также на рынке появились Retina-дисплеи с высокой плотностью пикселей (DPI), где обычные картинки выглядят размытыми. Растет доля мобильного трафика, и крупные ресурсы нацелены на экономную загрузку изображений. Рассмотрим, как решают эти проблемы на сайтах Apple, Tilda и блог-платформе Medium.
Apple в своих продуктах делает ставку на дизайн, уделяя большое внимание исследованию отображения картинок. На сайте Apple — на момент написания этой статьи — оптимизация изображений обеспечивается за счет возможностей CSS, точнее, background-image. Ключевым принципом этого подхода является фиксированная ширина картинки между брейкпоинтами, то есть отказ от “резиновых” картинок. Рассмотрим на примере одного из изображений с сайта www.apple.com/mac
Для кроссбраузерности используются сразу три способа задать плотность пикселей экрана:
-webkit-min-device-pixel-ratio: 1.5
min-resolution: 1.5dppx
min-resolution: 144dpi
Какие плюсы и минусы данного подхода?
Скорее всего, вы уже используете один из CSS препроцессоров, таких как SCSS, LESS, PostCSS или Stylus — и они способны значительно упростить вам жизнь. Попробуем оптимизировать код с сайта Apple с помощью SCSS. Для медиа-выражений возьмем миксин media и напишем свой миксин для картинки:
Зададим переменные:
Получаем такой SCSS, который компилируется один в один в CSS от Apple:
Это отличный подход, с которым можно жить, если у вас не очень много картинок или они редко меняются.
Тильда — это конструктор сайтов, который предусматривает разные компоненты для отображения картинок. Рассмотрим самый простой способ “ленивой” загрузки картинок. Как выглядит html до загрузки картинки:
Эффект размытости достигается за счет того, что превью имеет маленький размер (20x20 пикселей), но с помощью css свойства cover картинка растягивается на всю ширину и высоту. Минус: такое размытые не очень красиво выглядит, зато осуществляется очень просто и не перегружает процессор.
После загрузки просто подставляется в inline style картинка в полном размере.
Это рабочий метод, если нужен простой компонент и не хочется заморачиваться с шестью и более картинками для разных разрешений экранов. Хорошо подходит для сайтов с большим количеством картинок.
Блог-платформа meduim.com произвела большой резонас в мире веб-картинок за счет красивого эффекта размытия для недогруженных изображений. Этот эффект достигается с помощью библиотеки stackblur-canvas.
HTML выглядит как сэндвич:
Это позволяет реализовать эффект плавного перехода. У картинки full-size изначально opacity:0 через css transition переходит в opacity:1.
Итак, если мы — не Apple, скорее всего, будет удобнее использовать ленивую загрузку картинок и эффект размытия. В данный момент есть как минимум три способа создать блёр эффект (и еще вариант от Tilda, но его мы рассматривать не будем, так как это не “настоящий” блёр).
Почему же все-таки стоит использовать stackblur-canvas для размытия если уже есть встроенный в браузер css фильтр для размытия?
В самом начале мы описали метод Apple и выяснили, что в css есть медиа-выражения и для размера экрана, и для плотности пикселей. И их можно использовать одновременно для выбора конкретной картинки для отображения. Существенным недостатком является только то, что картинка на фоне не “знает” своего соотношения сторон, не может быть “резиновой”. Кроме того, с точки зрения семантики веба использовать фон неправильно для отображения всех подряд картинок.
Тег img может иметь srcset атрибут, но вы должны выбрать размер экрана или плотность пикселей — комбинировать эти параметры нельзя.
Тег picture лишен этого недостатка, а также, как и img, “знает” свое соотношение сторон. Уже сейчас его поддерживают 92% браузеров. Этот тег подходит для тех, кто может себе позволить не подстраиваться под IE.
Наконец, поговорим о реализации ленивой загрузки картинок. Классическим подходом является использование комбинации событий scroll, resize и orientationchange и вычисление позиции конкретной картинки относительно видимой области экрана.
В 2019 году заменить набор костылей можно специальным API браузера — intersection observer. Его поддерживают все современные браузеры. Для старых iOS и IE стоит использовать официальный полифил от w3c.
Итак, у нас уже есть тег picture, который имеет хорошую поддержку браузерами, но остается проблема ленивой загрузки. Нам все еще нужно опираться на наши собственные реализации на основе intersection observer или комбинации JS событий. Когда браузеры предложат нативное решение? Например, Chrome 75 версии обещает предоставить поддержку свойства loading для тегов img и iframe. Вы можете обновить ваши компоненты для нативной поддержки ленивой загрузки, проверив наличие свойства в прототипе картинки: в случае успеха вы можете отказаться от собственного кода ленивой загрузки, так как все будет сделано браузером за вас!
Какой способ отображения лучше всего подойдет для картинок, загружаемых пользователями? Как правило, стоит все делать как можно проще, не заставляя его загружать картинки отдельно для телефона, планшета и десктопа. Итак, мы имеем только одну картинку для всех устройств. Для такой ситуации отлично подойдет подход Tilda (для максимальной простоты) или Medium — если вы хотите сделать настоящий эффект размытия, возможно, заменив stackblur-canvas на стандартный css: blur() для большей производительности.
До сих пор нет единственного, “самого правильного” способа отображать картинки. Если вам нужно обязательно поддерживать старые браузеры и IE, то ваш выбор — это background-image с медиа-выражениями и img для простых случаев. Для новых проектов бывает удобнее заменить background-image на picture. Если вы хотите делать красивую ленивую загрузку, попробуйте stackblur-canvas, а если вам важна производительность — то css фильтры или растянутые картинки на фоне в стиле тильды.
Apple: background-image power user
Apple в своих продуктах делает ставку на дизайн, уделяя большое внимание исследованию отображения картинок. На сайте Apple — на момент написания этой статьи — оптимизация изображений обеспечивается за счет возможностей CSS, точнее, background-image. Ключевым принципом этого подхода является фиксированная ширина картинки между брейкпоинтами, то есть отказ от “резиновых” картинок. Рассмотрим на примере одного из изображений с сайта www.apple.com/mac
<figure class="imac-image" data-progressive-image=""></figure>
.section-imac .imac-image {
width:939px;
height:631px;
background-size:939px 631px;
background-repeat:no-repeat;
background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_large.jpg");
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx), (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
.section-imac .imac-image {
background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_large_2x.jpg")
}
}
@media only screen and (max-width: 1068px) {
.section-imac .imac-image {
width:795px;
height:536px;
background-size:795px 536px;
background-repeat:no-repeat;
background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_medium.jpg")
}
}
@media only screen and (max-width: 1068px) and (-webkit-min-device-pixel-ratio: 1.5), only screen and (max-width: 1068px) and (min-resolution: 1.5dppx), only screen and (max-width: 1068px) and (min-resolution: 144dpi) {
.section-imac .imac-image {
background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_medium_2x.jpg")
}
}
@media only screen and (max-width: 735px) {
.section-imac .imac-image {
width:365px;
height:246px;
background-size:365px 246px;
background-repeat:no-repeat;
background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_small.jpg")
}
}
@media only screen and (max-width: 735px) and (-webkit-min-device-pixel-ratio: 1.5), only screen and (max-width: 735px) and (min-resolution: 1.5dppx), only screen and (max-width: 735px) and (min-resolution: 144dpi) {
.section-imac .imac-image {
background-image:url("/v/mac/home/af/images/overview/hero/imac__dlz2ciyr6hm6_small_2x.jpg")
}
}
Для кроссбраузерности используются сразу три способа задать плотность пикселей экрана:
-webkit-min-device-pixel-ratio: 1.5
min-resolution: 1.5dppx
min-resolution: 144dpi
Какие плюсы и минусы данного подхода?
Плюсы
- Простота реализации. Это, пожалуй, самый понятный способ для изменения картинки под определенные размеры и плотность экрана.
- Покрытие всех проблем: решается вопрос размера экрана и плотности пикселей.
- Нет прыгающей верстки, благодаря фиксированным заранее размерам.
- Можно использовать встроенные в CSS возможности работы с фонами (blend-mode для эффектов, position — обрезать картинку, если нужно).
Минусы
- Нельзя сделать “резиновую” картинку. Фиксированные размеры определяют соотношение сторон, поэтому при попытках сжатия или растяжения у нас обрезается картинка или появляется пустое место внизу. Да, можно использовать padding-bottom в процентах вместо размера, но это уже обходное решение, CSS хак, который усложняет базовую концепцию.
- Много CSS кода. Да, CSS легко читается, но при большом количестве брейкпоинтов и картинок размер CSS может увеличиваться до бесконечности (см. ниже “CSS препроцессоры спешат на помощь”).
- Нет контроля над загрузкой картинок. Если вы используете такой подход в чистом виде, то будете грузить все картинки на странице одновременно. И CSS, в отличие от img, не имеет onload колбеков, позволяющих контролировать, загрузились картинки или нет.
- Требует дотошной аккуратности. Вам придется выставить размеры для всех картинок. И что не менее важно, при замене одной картинки на другую нужно будет править CSS под новые размеры.
CSS препроцессоры спешат на помощь
Скорее всего, вы уже используете один из CSS препроцессоров, таких как SCSS, LESS, PostCSS или Stylus — и они способны значительно упростить вам жизнь. Попробуем оптимизировать код с сайта Apple с помощью SCSS. Для медиа-выражений возьмем миксин media и напишем свой миксин для картинки:
@mixin image($width, $height, $url) {
width: $width;
height: $height;
background-size: $width $height;
background-repeat:no-repeat;
background-image:url($url);
}
Зададим переменные:
$breakpoints: (
'phone': 735px,
'tablet': 1068px
);
$media-expressions: (
'screen': 'only screen',
'retina': '(-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 1.5dppx), (min-resolution: 144dpi)'
);
Получаем такой SCSS, который компилируется один в один в CSS от Apple:
.section-imac .imac-image {
@include image(939px, 631px, "image_large.jpg");
}
@include media('screen', 'retina') {
.section-imac .imac-image {
@include image(939px, 631px, "image_large2x.jpg");
}
}
@include media('screen', '<=tablet') {
.section-imac .imac-image {
@include image(795px, 536px, "image_medium.jpg");
}
}
@include media('screen', '<=tablet', 'retina') {
.section-imac .imac-image {
@include image(795px, 536px, "image_medium_2x.jpg");
}
}
@include media('screen', '<=phone') {
.section-imac .imac-image {
@include image(365px, 246px, "image_small.jpg");
}
}
@include media('screen', '<=phone', 'retina') {
.section-imac .imac-image {
@include image(365px, 246px, "image_small_2x.jpg");
}
}
Резюме
Это отличный подход, с которым можно жить, если у вас не очень много картинок или они редко меняются.
Tilda: background-image с blur эффектом
Тильда — это конструктор сайтов, который предусматривает разные компоненты для отображения картинок. Рассмотрим самый простой способ “ленивой” загрузки картинок. Как выглядит html до загрузки картинки:
<div data-original="https://static.tildacdn.com/image.jpg" style="background: rgba(0, 0, 0, 0) url(https://static.tildacdn.com/image-small.jpg) no-repeat scroll center center / cover;">
</div>
Эффект размытости достигается за счет того, что превью имеет маленький размер (20x20 пикселей), но с помощью css свойства cover картинка растягивается на всю ширину и высоту. Минус: такое размытые не очень красиво выглядит, зато осуществляется очень просто и не перегружает процессор.
После загрузки просто подставляется в inline style картинка в полном размере.
Плюсы
- Ленивая загрузка.
- Легко поддерживать.
Минусы
- Одна и та же картинка для любых устройств. Адаптивность реализована только в пределах стилизации (картинка просто обрезается по-разному).
- Некрасивый эффект размытия.
- Нет перехода между размытием и полным разрешением.
Резюме
Это рабочий метод, если нужен простой компонент и не хочется заморачиваться с шестью и более картинками для разных разрешений экранов. Хорошо подходит для сайтов с большим количеством картинок.
Medium: canvas blur
Блог-платформа meduim.com произвела большой резонас в мире веб-картинок за счет красивого эффекта размытия для недогруженных изображений. Этот эффект достигается с помощью библиотеки stackblur-canvas.
HTML выглядит как сэндвич:
<img src="placeholder.jpg">
<canvas>
<img src="full-size.jpg">
Это позволяет реализовать эффект плавного перехода. У картинки full-size изначально opacity:0 через css transition переходит в opacity:1.
Blur Battle: CSS vs SVG vs Canvas
Итак, если мы — не Apple, скорее всего, будет удобнее использовать ленивую загрузку картинок и эффект размытия. В данный момент есть как минимум три способа создать блёр эффект (и еще вариант от Tilda, но его мы рассматривать не будем, так как это не “настоящий” блёр).
Почему же все-таки стоит использовать stackblur-canvas для размытия если уже есть встроенный в браузер css фильтр для размытия?
- Проблемы. CSS и SVG фильтры по умолчанию размывают края картинки. Для svg фильтра есть специальное решение, для встроенного filter: blur() придется использовать обрезание краев.
- Performance. Stackblur-canvas очень быстро работает, css фильтры работают еще быстрее, поэтому при большом количестве размываемых картинок выбор однозначно за css фильтрами.
- Красота. Здесь субъективно, но на мой взгляд stackblur более красиво выглядит. У svg фильтра бывают некрасивые артефакты.
Тег Picture — вершина эволюции
В самом начале мы описали метод Apple и выяснили, что в css есть медиа-выражения и для размера экрана, и для плотности пикселей. И их можно использовать одновременно для выбора конкретной картинки для отображения. Существенным недостатком является только то, что картинка на фоне не “знает” своего соотношения сторон, не может быть “резиновой”. Кроме того, с точки зрения семантики веба использовать фон неправильно для отображения всех подряд картинок.
Тег img может иметь srcset атрибут, но вы должны выбрать размер экрана или плотность пикселей — комбинировать эти параметры нельзя.
Тег picture лишен этого недостатка, а также, как и img, “знает” свое соотношение сторон. Уже сейчас его поддерживают 92% браузеров. Этот тег подходит для тех, кто может себе позволить не подстраиваться под IE.
Intersection Observer API
Наконец, поговорим о реализации ленивой загрузки картинок. Классическим подходом является использование комбинации событий scroll, resize и orientationchange и вычисление позиции конкретной картинки относительно видимой области экрана.
В 2019 году заменить набор костылей можно специальным API браузера — intersection observer. Его поддерживают все современные браузеры. Для старых iOS и IE стоит использовать официальный полифил от w3c.
Взгляд в будущее: чего еще не хватает для полного счастья
Итак, у нас уже есть тег picture, который имеет хорошую поддержку браузерами, но остается проблема ленивой загрузки. Нам все еще нужно опираться на наши собственные реализации на основе intersection observer или комбинации JS событий. Когда браузеры предложат нативное решение? Например, Chrome 75 версии обещает предоставить поддержку свойства loading для тегов img и iframe. Вы можете обновить ваши компоненты для нативной поддержки ленивой загрузки, проверив наличие свойства в прототипе картинки: в случае успеха вы можете отказаться от собственного кода ленивой загрузки, так как все будет сделано браузером за вас!
if ('loading' in HTMLImageElement.prototype)
CMS и пользовательский контент
Какой способ отображения лучше всего подойдет для картинок, загружаемых пользователями? Как правило, стоит все делать как можно проще, не заставляя его загружать картинки отдельно для телефона, планшета и десктопа. Итак, мы имеем только одну картинку для всех устройств. Для такой ситуации отлично подойдет подход Tilda (для максимальной простоты) или Medium — если вы хотите сделать настоящий эффект размытия, возможно, заменив stackblur-canvas на стандартный css: blur() для большей производительности.
Итоги
До сих пор нет единственного, “самого правильного” способа отображать картинки. Если вам нужно обязательно поддерживать старые браузеры и IE, то ваш выбор — это background-image с медиа-выражениями и img для простых случаев. Для новых проектов бывает удобнее заменить background-image на picture. Если вы хотите делать красивую ленивую загрузку, попробуйте stackblur-canvas, а если вам важна производительность — то css фильтры или растянутые картинки на фоне в стиле тильды.
DSolodukhin
И именно поэтому вы код отображаете «неправильными» картинками?
SSul Автор
Спасибо за внимательность, поправили)