Руководство по WebKit-атрибуту srcset в тэге img

Original author: WebKit.org
  • Translation
Свободный движок WebKit теперь поддерживает srcset атрибут изображений, IMG элементов (официальная спецификация от W3C). Это позволяет вам, как разработчику, использовать картинки с высоким разрешением для пользователей использующих ретина-дисплей без ущерба для остальных пользователей. Также важно отметить о наличие изящного решения для браузеров, которые еще не поддерживают данный функционал.

Обратите внимание, в необходимости использовать последние ночные сборки WebKit.


Возможно, вы знаете, что WebKit поддерживает -webkit-image-set CSS функцию чуть более года (Safari начиная с 6 версии и Google Chrome начиная с 21 версии). С помощью данной функции появилась возможность задавать в css-свойствах изображения в зависимости от разрешения экрана, или другого устройства воспроизведения, каждое с модификатором 1х, 2х.

.class {
  /* задаем фоновое изображение, если браузер не поддерживает image-set */
  background-image: url(image-set-not-supported.png);
  /* задаем фоновые изображения, для различных разрешений */
  background-image: -webkit-image-set(url(low-res-image.jpg) 1x, url(high-res-image.jpg) 2x);
}

Это позволит браузеру выбрать наилучший вариант для конкретного устройства пользователя. Прежде, для поддержки больших картинок у вас было несколько путей развития, каждый с определенными недостатками. Вы могли дублировать CSS код. Вы могли использовать JavaScript для запроса (window.devicePixelRatio) количества точек на px. (device pixel ratio) и обновлять картинки после загрузки страницы. Или вы могли всегда отдавать большие картинки, тем самым заставляя некоторых пользователей грузить лишнее. И если вам необходимы были картинки с разными разрешениями приходилось уточнять связанные CSS свойства, такие как background-size как часть border-image. Это раздражало.

К счастью, -webkit-image-set позволяет написать вам одно простое правило, тем самым заставив браузер самому определять более подходящую картинку и как следствие её и загружать.

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

<img src="normal-image.jpg" srcset="better-image.jpg 2x">

Обратили внимание на “2x” после “better-image.jpg”? Это говорит браузеру, что для экранов устройств с devicePixelRatio = 2 и более, необходимо использовать better-image.jpg взамен normal-image.jpg. И если экран устройства не «ретина», браузер обратится к атрибуту src. Вы также можете задавать значение 1х атрибута srcset, как в примере ниже.

Srcset атрибут
Ниже приведено изображение — img элемент с обоими атрибутами: src и srcset. Существует стиль устанавливающий размер картинки в 400x400px. В браузерах без поддержки srcset, будет использоваться значение src, а следовательно загрузится стандартная картинка:
image

На обычных экранах будет загружаться вариант 1х srcset атрибута:
image

На ретина дисплеях будет загружаться вариант 2х srcset атрибута:
image

HTML код примера:
<img src="image-src.png" srcset="image-1x.png 1x, image-2x.png 2x">




Больше об srcset можно прочитать в официальной спецификации. Обратите внимание, что на данный момент только WebKit поддерживает «коэффициенты разрешений» — 1x, 2x, 3x. Как и любой новый функционал, WebKit может содержать ошибки, поэтому будьте бдительны. В частности, вы должны убедиться, что WebKit загружает минимальные ресурсы для страницы, потому что это одна из целей этой функции.

Особая благодарность членам сообщества WebKit Romain Perier и Yoav Weiss которые внесли важный вклад (r153624, r153733) в развитие данного функционала.

P.S. Замечания по переводу присылайте в личку.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 61

    0
    интересно, при печати страницы на бумажке, будет ли браться 2x версия картинки если монитор у компа — не-ретина?
      0
      В идеале следует делать отдельные стили для странице печати. А по-умолчанию, хороший вопрос, нужно проверить.
        0
        При печати по стандарту рекомендуется использовать физические единицы измерения. И здесь браузер просто на ура знает о вашем экране все. Физические значения он выводит точно независимо от плотности экрана. Можете проверить линейкой заданные в css значения в mm.

        Что касается «как» это будет решать браузер, пытаясь заполнить формат листа. Ведь это его драйвер печати все равно будет пересчитывать px в физические единицы. Так как он и сейчас не плохо справляется, не думаю, что придется ему долго думать как же печатать тоже самое только большое.
          +1
          не удивлюсь, если все будет «как всегда».

          Помнится при печати страницы с встроенным svg кто-то из браузеров когда-то тупо рендерил svg под DPI экрана и потом печатал с таким низким DPI картинку.

          ActiveX -компоненты вроде тоже так в MSIE раньше печатались (особенно было обидно если это был компонент для графиков, например).
            0
            Все что идет на печать рекомендуется указывать в физ. единицах. Для svg нужно было указать размеры в мм и все было бы норм я думаю. Браузер не телепат же)
            www.w3.org/Style/Examples/007/units
            • UFO just landed and posted this here
            0
            И здесь браузер просто на ура знает о вашем экране все. Физические значения он выводит точно независимо от плотности экрана. Можете проверить линейкой заданные в css значения в mm.

            Весьма спорно. Как минимум он знает только то, что ему сообщила ОС, а как максимум может сам решить поставить 96dpi, ОС даже не спрашивая.
            • UFO just landed and posted this here
            +1
            Честно говоря очень противоречивые чувства вызывает эта возможность. Я не удивлюсь, если потом появятся не просто 2x, но и дробные значения типа 2.1, 2.2 и т.д. Плотность и сейчас на разных устройствах сильно отличается, просто раньше на этом особо не заострялось внимание. Считалось, что достаточно знать о разрешении. Хотя, может пронесет и все будет придерживаться некого общего стандарта.

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

            PS: Как выше точно подметили, изображение для печати тоже под вопросом.
              0
              Я думаю, через какое-то время все перейдут на «ретиновую» плотность, и вопрос отпадёт сам собой: всё, что меньше, выйдет из употребления, а больше просто не нужно.
                +2
                В этом и странность. То есть в итоге мы перейдем просто на параметр srcset вместо src, но будем там писать всегда 2x. Зачем в итоге тогда было городить огород, если все и так в src начали бы помещать картинки двойной плотности? А сейчас нужно еще и девайсы с такими экранами научить понимать новый параметр. А как мы через JS будем с этими картинками работать? Не придется ли самому еще эту строку разбирать в итоге?
                  0
                  Ну собственно, да, тоже не вижу особого смысла: у телефонов (т.е. как раз там, где актуальны ограничения по трафику) высокая плотность как раз быстрее зафиксируется, чем у мониторов. В итоге, получится, а для кого старались?
                  По джаваскрипту — ну тут просто библиотеки вроде jQuery подтянутся. Разбор строки — всё-таки куда менее затратная операция, чем обращение к элементам DOM, так что с этой точки зрения ничего страшного тут не случится.
                  +2
                  нет «ретиновой плотности». Есть просто dpi. Просто усилиями MS, которая забила на значение dpi в настройках системы, все делали что попало и было точно известно, что больше 90-100 dpi железки делать не стоит. А тут подтянулись андроиды и иосы, в которых dpi может быть практически любой, и начался бум нормальных пикселей.

                  Этой революции я давно ждал. Осталось дождаться десктопных мониторов с 300dpi — и революция свершится. Но при этом у каждой железки будет свой dpi.
                    0
                    В определённом смысле, да, но плотность будет примерно одинаковой (как была примерно одинаковой до появления ретины). То есть в итоге скорее всего получится условные x1 для легаси-устройств (а они понятно ещё долго будут в употреблении) и условные x2 для современных. Потом окажется, что x1 не нужен и останется только x2. Я к тому, что другие значения вряд ли кому понадобятся.
                      0
                      никогда не говори «никогда». Если глассы взлетят, то может потребоваться и сильно больше 1200 dpi. Да и вообще, чо мы нищеброды, на 300 dpi мониторах сидеть? Даёшь 600, да 1200 для понтовых чуваков.
                        0
                        Про глассы — да, не подумал, согласен.
                          0
                          К глассам же неприменимо DPI в обычном смысле. А «виртуальных» DPI там наоборот мало очень.
                        +1
                        В Windows DPI тоже мог быть «практически любой», еще со времен Win95 — всегда можно было выставить DPI вплоть до 480 (настройка «500%»; за «100%» принимается 96dpi). То, что на это забивал софт (причем в первую очередь именно сторонний) — это как бы не «усилиями MS», а скорее наоборот. Про то, как именно писать DPI-aware софт, на MSDN всегда была куча статей.

                        Напомню также, что WPF — UI-фреймворк, который не просто учитывал системное значение DPI, но форсил это на все написанные на нем приложения (вплоть до того, что в WPF пиксели, как в CSS сегодня, не были физическими пикселями) — вышел в еще 2006-м.
                          0
                          При этом сама MS в своих продуктах вполне на этот показатель забивала. Например, word имел своё представление о dpi на экране и том, как правильно рендерить шрифты по целым пикселам.

                          В winapi практически не было методов грамотно совместить текст и изображение на экране без пиксельной привязки. Любой человек, который пробовал ключить 120dpi быстро обнаруживал не только, что посторонний софт начинает писать 24 кеглем в 12-пиксельной менюшке, но и в самой винде местами становилось уродливо до невозможности (тот же трей, например).
                            +2
                            Долгое время забивала, да. Но к висте я уже не помню никакого софта от MS, который бы не понимал high DPI.

                            Чтобы совместить текст и изображение (и вообще все, что угодно) на экране с учетом DPI, достаточно иметь один-единственный API — тот, который вернет вам этот самый DPI. Потом уже можно самому пересчитывать все в пиксели и растягивать картинки. Да, WinAPI здесь не помощник (хотя были DPI-независимые dialog units — но это полумера), но он всегда был очень низкоуровневой вещью, и большинство пользовались обертками. А вот то, что почти все обертки игнорировали DPI, это грустно, да. Кстати, помните твипы в VB6? То, в чем измерялся размер всех форм и виджетов на них? Они ведь тоже были логическими…
                      0
                      Не бывает плотности 2,2 и быть не может. Это коэффициент, показывающий соотношение физических пикселей к логическим. 2,2 означает, что любая картинка будет мылиться.
                        0
                        Если это «коэффициент, показывающий соотношение физических пикселей к логическим», то какая должна быть плотность на ретинамакбуке 13" в режиме 1680x1050? Ответ — 2560 / 1680 = 1,52. Так что 2 — это соотношение одних логических пикселей (классического разрешения, в которых удобно мерить) к другим логическим пикселям (разрешение, в котором рисует видеокарта). И на многих Андроидах оно действительно дробное.
                          0
                          Ничего подобного. window.devicePixelRatio показывает 2.
                            +1
                            Ну, на ретине показывает 2, потому что это не «коэффициент, показывающий соотношение физических пикселей к логическим». А на многих Андроидах оно дробное.
                              0
                              Ок, ваше определение какое?
                                +3
                                Я бы назвал это коэффициент масштабирования интерфейса, без привязки какие где пиксели. Но возвращаясь к первоначальному комментарию: «Не бывает плотности 2,2 и быть не может. 2,2 означает, что любая картинка будет мылиться» хочу сказать, что а) бывает; б) даже если бы никто так не делал, оно все равно могло бы быть; в) картинка, да, мылится. Точно так же как мылятся любые картинки в вебе на экране айфона в силу того, что масштабирование браузера плавное и масштаб очень-очень редко бывает ровно 100% или 200%.
                          0
                          Что значит логических? Сколько логических пикселей в одном физическом?
                            –2
                            Ну почитайте, что ли что это такое. Что значит «сколько»? Сколько сделает производитель, столько будет.
                              +1
                              Думаете, что отправить меня в поиск — это хороший способ ответа? Ну хорошо, тогда я вам дам пару цитат для размышления:

                              The Nexus One has a physical resolution of 480x800, but the Android WebKit team decided that the optimal width for a web page viewed in portrait mode was still 320px. Thus the dips abstraction layer remained 320 pixels wide, and devicePixelRatio became 480/320 = 1.5.
                              [...]
                              The Galaxy Nexus has a significantly upped pixel count of 720x1200. The Android team decided to up the width of the dips layer, too, to 360px. This makes devicePixelRatio 720/360 = 2.
                              [...]
                              Opera, however, disagreed, deciding to stick with a dips width of 320px. This yields a devicePixelRatio of 720/320 = 2.25


                              Источник.

                              То есть, в итоге, я зря волновался за появление таких значений — они уже используются везде фактически.
                                0
                                И, чтобы не возникло впечатления, что это такой «плохой Android всегда портит», добавлю, что выше homm верно указал. На Retina MacBook если поставить разрешение 1920x1200, то devicePixelRatio будет показывать 2, но в реальности оно будет равняться 1,5. Короче дело с srcset совсем плохо. Похоже это будет работать только с Apple, которые сами же и будут указывать у себя кривые данные, чтобы все работало.
                              0
                              а как вам такие значеия devicePixelRatio: 1.325, 1.6, 1.7, 2.37? :)
                              Источник — en.wikipedia.org/wiki/List_of_displays_by_pixel_density
                                0
                                Неделю назад уже всё разъяснили и переубедили.
                            +3
                            У меня возник вопрос. А почему было просто не дать доступ к атрибуту src из css? Позволив писать примерно так:
                            @media screen and (min-resolution: 440dpi) { 
                                img {
                                    src : attr(data-bigimg);
                                }
                            }
                            


                            А в html:

                            <img src="image.jpg" data-bigimg="image_large.jpg">
                            <img src="image-some.jpg" data-bigimg="image-some_large.jpg">
                            


                            Мне кажется это было бы более гибкое решение.
                              0
                              В CSS вообще нельзя изменять аттрибуты HTML элементов (src — аттрибут). Ведъ CSS должен заниматься оформлением контента, и не затрагивать смысл (и структуру) контента. Иначе, например, была HTML страничка:
                              <html>Вот такой у нас рост прибыли: <img src="график.png"></html>
                              

                              а после применения CSS стиля:
                              img { src: котики.jpg }
                              

                              контент статьи о финансах будет испорчен. И это не правильно.
                                0
                                Я думаю не все так трагично. Ведь эти вещи на совести разработчика поддерживающего проект. Мне кажется мой вариант все же с родни свойства content у псевдо-элементов. Если опираться на Вашу логику то можно написать нечто вроде:
                                *:before {
                                    content: 'Before';
                                }
                                


                                И тому подобное плюс еще упаковку нехитрых манипуляций. И так же существенно подпортить внешний вид документа
                                  0
                                  С этим приемущественно согласен. Но Вы предлагаете ввести в CSS возможности, которых ранее пытались избежать по причинам большой опасности для малоопытных верстальщиков.
                                  Введение content:, вероятно, было принято в результате отсутствия других, достаточно удобных, вариантов. А в случае с srcset есть достаточно удобный(мне он кажется удобней вашего) вариант не противоречащий основным принципам взаимодействия CSS и HTML. Да и он обычно не подменяет исходные данные, а только дополняет оформление текстом.
                                    0
                                    Я не спорю, вполне могу упускать что-то. Но вот это решение с атрибутом srcset решает крайне узкую задачу (По крайней мере исходя из того, что написано в статье).
                                    <img src="image-src.png" srcset="image-1x.png 1x, image-2x.png 2x">
                                    

                                    То есть:
                                    devicePixelRatio = 1 => image-1x.png
                                    devicePixelRatio = 2 => image-2x.png
                                    Все… На этом функционал закончился.
                                    Но есть ситуации которые требуют более тонкой настройки, к примеру, портфолио фотографа. Для получения наилучшего качества, необходимо создать, скажем, три версии фото под ширину экрана 1024px, 1600px, 1920px, к примеру. Отдавать FullHD картинку в экран 1024px — лишний трафик, плюс весьма сомнительное качество картинки в следствии неидеальных алгоритмов браузера. Как отдать соответствующую картинку в зависимости от разрешения с помощью предложенного атрибута, видимо, никак (могу ошибаться, разумеется). В моем варианте я могу написать так (само собой пример грубый, только чтобы передать смысл):
                                    @media screen and (max-width: 1600px) { 
                                        img {
                                            src : attr(data-i1024);
                                        }
                                    }
                                    @media screen and (min-width: 1600px) { 
                                        img {
                                            src : attr(data-i1600);
                                        }
                                    }
                                    @media screen and (min-width: 1920px) { 
                                        img {
                                            src : attr(data-i1920);
                                        }
                                    }
                                    

                                    <img src="image_1920.jpg" data-i1024="image_1024.jpg" data-i1600="image_1600.jpg" data-i1920="image_1920.jpg">
                                    


                                    Кода, конечно, больше, но в руках максимум свободы.
                                    Но опять же я не утверждаю, что это лучшее решение, просто как вариант :)
                                      +1
                                      CSS вообще не похож на «Максимум свободы». В нем нет многих элементарных вещей благодаря чему многие используют SASS\LESS\...\jQuery. А максимум свободы можно получить используя JavaScript (или, если нужно больше, то Assembler и т.д.).
                                      Стоит отметить свое мнение относительно сабжа: товарищи из Apple пытаются заведомо запутать ситуацию, учитывая только целые device pixel ratio. Но с учетом префикса -webkit- это лучше чем отсутствие решений.
                                      А мне полным решением данной проблемы видится такое:
                                      Ввести новое семейство css свойтсв detalization ниже основные примеры.
                                      min-image-detalization: 1px — означает что в 1px экрана должно загружаться не менее одного пикселя изображения.
                                      max-image-detalization: 3dpi — означает что в 3px*devicePixelRatio не обязательно загружать не более одного пикселя изображения.
                                      background-detalization: auto — устанавливается браузером и его настройками исходя из текущей скорости интернета.
                                      detalization: best — всегда подгружать лучшую детализацию.
                                      Научить браузеры, сервера и веб-мастеров по умному использовать прогрессивное сжатие jpeg и аналогов. Так чтобы браузер мог запросить у сервера только первую(первые) часть jpeg-файла, а за тем если необходимо — догружать остальное.
                                      Использовать атрибут srcset=«image-l.gif 100X66 low; image-m.png 300x200; image-b.png 600x400 best» для форматов не поддерживающих прогрессивное сжатие.
                                      Использовать background-image: srcset(url(image-l.gif) 100x66, url(image-b.png) best) для бекграундов не поддерживающих прогрессивное сжатие.
                                      Но проще продвинуть нейроинтерфейс в котором отпадут подобные проблемы.
                                      • UFO just landed and posted this here
                                  0
                                  Может быть потому что CSS отвечает только за представление контента, а не его модификацию. Поэтому все content и псевдоэлементы не очень подходят под определение CSS.
                                    0
                                    Представление порой требует и дополнительных элементов и даже условий. Скажем на каком уровне лучше решать задачу «Если число отрицательное, то красим красным цветом, если положительное — зеленым»?
                                      0
                                      На уровне сервера, а если это динамика — на уровне JS, и всё это прописывая соотв. классы.
                                        0
                                        Вот в этом и проблема — мы сейчас так и делаем. Либо генерируем нужный класс сразу на сервере, либо динамически что-то добавляем скриптом. Но, по идее, эту задачу, как оформительскую, можно было бы возложить на CSS, добавив небольшое условие на проверку значения. На самом деле условия в CSS есть и сейчас уже:

                                        tr:nth-child(2n) {
                                            background: #f0f0f0;
                                        }
                                        
                                        div:eq(2) {
                                            background: red;
                                        }
                                        


                                        И псевдоклассы эти очень полезны. Но мы пока не добрались до проверки самого содержимого. Вам не кажется, что она весьма уместна в таких случаях, как я указал выше?
                                        • UFO just landed and posted this here
                                            +1
                                            Прошу прощения, что-то действительно перестарался. eq — действительно jQuery-отсебятина. Надо же… не думал, что можно селекторы эмулировать через JS. Спасибо за то, что открыли глаза. :)
                                            0
                                            Не, я против перекладывания тяжёлой логики на CSS. :nth-child() удобен и отлично справляется со своей задачей. А почему? Потому что он простой, его легко понять, а когда эти селекторы усложняются то и сам код понимать становится труднее. Я уже не говорю про загадки текущих свойств в CSS. Всю логику я бы реализовал отдельным модулем, как media queries, к примеру.
                                              0
                                              Я согласен, что-то серьезное закладывать — большое зло. Эдак можно и до функций дойти в CSS… Но некоторые простые вещи действительно стоит развивать. Вот, как выше отметили уже, оказывается в CSS нет даже поддержки селектора :eq(2), а штука весьма простая. Содержимое привести к числу и проверить на "<", ">", "=", "!=" — тоже элементарно. Про отдельный модуль не понятно. Куда и как его выносить?
                                                +2
                                                Вот, как выше отметили уже, оказывается в CSS нет даже поддержки селектора :eq(2), а штука весьма простая.

                                                Почему же? Можно написать :nth-child(2)
                                                jsfiddle.net/alexriz/yxNjv/
                                                  0
                                                  да, все верно. то есть :eq — просто более краткая запись. Я как-то привык ей уже пользоваться, не обратив внимание, что это уже идет реализация от jQuery.
                                                  0
                                                  Ну от чего же большое зло? XPath/XQuery вполне годные вещи для навигации и выборки нод из DOM. Простейшие селекторы не сложнее CSS (если не проще), а сложные намного мощнее.
                                                    0
                                                    Так это для выборки. А мы же про оформление сейчас говорим.
                                              +1
                                              Атрибут class=«negative» мало чем отличается по сути от атрибута color=«red» — меняя данные нам нужно менять и атрибут, а на уровне HTML/CSS мы не можем сделать автоматическую смену цвета для отрицательных значений. Да и с JS не так просто это делается.
                                        • UFO just landed and posted this here
                                            0
                                            Ахренеть! Как мне теперь с этим жить? Ведь это еще CSS Level 2. Нужно не забыть проверить, с какого края света солнце всходит.
                                            • UFO just landed and posted this here
                                                0
                                                Успокоили. Теперь можно не дожидаться восхода, а ложиться спать.
                                          0
                                          А как придется в jquery писать, если надо картинку заменить (допустим, флаг США на флаг РФ) — придется атрибут src и атрибут srcset менять?
                                            0
                                            > Тестируем свой браузер. Видите синюю картинку — не поддерживает.
                                            > Красную — браузер выбрал вариант 1х, зеленую — браузер выбрал вариант 2х.

                                            Естественно, на Хабре вся разметка фильтруется и лишние атрибуты удаляются. Так что этот тест не работает. armid, прошу убрать его, дабы не вводить людей в заблуждение.
                                              +1
                                              Готово.
                                              +1
                                              Опять велосипед. Была же отличная статья, по итогам которой оказалось удобнее всего использовать 2x картинки с 30%, которые весят как 1x с 80% но при этом чётче. При этом работают абсолютно везде, даже в браузерах с увеличением по ctrl+, только лишь один pagespeed зря ругается.
                                                0
                                                Как я понимаю, это относится только к JPG.

                                              Only users with full accounts can post comments. Log in, please.