Обеспечение лучшей резервной поддержки SVG и управление оформлением с помощью элемента <picture>

Перевод статьи Better SVG Fallback and Art Direction With The <picture> Element с сайта http://sarasoueidan.com/, автор — Сара Суэйдан.

Помимо использования SVG в CSS в качестве фонового изображения, вы можете использовать SVG и для изображений в контенте HTML с помощью одной из нескольких техник для их встраивания. Каждая из них имеет свои преимущества и сценарии использования.  Если вам не нужна интерактивность или возможность подключения внешних стилей, стандартным способом загрузки SVG-изображения будет тег <img>, но у него есть один недостаток: на сегодняшний день для создания запасных вариантов изображений и/или для управления оформлением страницы необходимо использовать JavaScript. В этой статье мы поговорим о лучшем методе для достижения этой цели: с помощью элемента <picture>.

Перед вами не учебник по использованию <picture>. На просторах интернета существует множество ресурсов со всей информацией, которую нужно знать об этом элементе. Поэтому если вы с ним не знакомы, обратитесь к последнему разделу этой статьи – там вы найдете список ресурсов, чтобы узнать всё, что необходимо. С другой стороны, эта статья не требует от вас особых навыков работы с <picture>. Как вы сами сможете убедиться, все примеры в ней в большинстве своём понятны без разъяснений.

<img> сегодня

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

Обычно, при загрузке SVG с помощью тега <img>, вы можете указывать запасные варианты и изменять источник изображения в зависимости от размера области просмотра, благодаря определению возможносей и медиазапросам при помощи JavaScript. Лично я в обоих случаях использовала бы Modernizr; конечно, если вы не отдаете всего одно изображение, т.к. в этом случае использование Modernizr было бы избыточным, и использование чего-то вроде:

<img src="logo.svg" onerror="this.src=logo-fallback.png;this.onerror=null;" />

…было бы проще и быстрее.

С помощью Modernizr можно определить, поддерживает ли браузер SVG, и в случае отсутствия поддержки использовать альтернативный src для изображения. URL для альтернативного изображения может храниться в специальном data-атрибуте. Этот подход, в частности, полезен в случаях, когда у вас на странице есть несколько изображений, требующих изменения src.

<img src="logo.svg" data-fallback="logo.png" />

С помощью Modernizr можно определить, поддерживает ли браузер SVG, после чего предпринять необходимые действия для обеспечения резервной поддержки, исходя из результатов этого теста:

if (!Modernizr.svg) {
  // вызвать запасной вариант и заменить им src у img
}

Кроме того, вы можете использовать метод Modernizr для обнаружения поддержки медиазапросов, чтобы менять источник изображения, исходя из ширины области просмотра, если вы хотите управлять оформлением страницы и не загружать то же большое SVG-изображение на экранах меньшего размера:

if (Modernizr.mq('(max-width: 640px)')) {
  // заменить источник изображения источником SVG меньшего размера
}

В этом случае, если вы используете определенное соглашение для присваивания имен изображениям, хранить URL в data-атрибутах не нужно. Например, если ваши изображения носят имена view-small.svg, view-big.svg, можно просто заменить часть view-* на необходимую с помощью JavaScript — этого будет достаточно. В этом случае, если вы захотите обеспечить запасные PNG или JPEG своему SVG и при этом предоставить несколько размеров для резервного изображения, соответствующих размерам области просмотра, вы тоже сможете использовать Modernizr, но задача немного усложнится. И что самое важное: для этого вам понадобится JavaScript.

Используя элемент <picture>, мы можем сделать всё это, и даже больше, не используя JavaScript. Ну, почти. Давайте продолжим.

Общий взгляд на <picture>

Элемент <picture> предоставляет нам лучший способ без использования JavaScript изменять изображения, которые мы отдаем, в зависимости от различных значений медиазапросов, и обеспечивать запасные варианты для браузеров, не поддерживающих SVG (или браузеров, которые по той или иной причине не могут загрузить SVG).

Загрузка SVG и обеспечение запасных вариантов для браузеров, не поддерживающих SVG

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

<picture>
    <source type="image/svg+xml" srcset="path/to/image.svg">
    <img src="path/to/fallback.png" alt="Описание изображения">
</picture>

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

В элементе <source> мы указываем, какое изображение(-я) нам необходимо. Мы указываем тип нужного нам изображения (SVG) в атрибуте type и URL этого изображения в атрибуте srcset. И это всё, что от нас требуется; вот так просто мы можем обеспечить резервную поддержку для SVG-изображения с помощью <picture> — без использования JavaScript.

Можно пойти еще дальше и указать несколько резервных изображений, которые будут учитывать разрешение экрана. Для этого необходимо указать эти изображения с помощью атрибута srcset элемента <img>. К примеру:

<picture>
    <source type="image/svg+xml" srcset="path/to/logo.svg">
    <img src="path/to/logo-1x.png" srcset="path/to/logo-2x.png 2x, path/to/logo-3x.png 3x" alt="Описание логотипа">
</picture>

После этого браузер сможет выбрать подходящее изображение, исходя из разрешения экрана. Это пригодится в тех случаях, когда вы отдаете изображение одного и того же размера (например, логотип с единственным размером), но хотите предоставить версии с 2x- и 3х-кратным увеличением для экранов с большим разрешением.

Если же вы хотите пойти еще дальше… С помощью атрибута sizes вы можете использовать медиазапросы в <img> для изменения размера запасного изображения на экранах разного размера, отдавая большее изображение на больших экранах и меньшее – на маленьких.

<picture>
    <source type="image/svg+xml" srcset="path/to/banner.svg">
    <img
      sizes="(min-width: 640px) 80vw, 100vw"
      srcset="banner-300.jpg 300w,
              banner-400.jpg 400w,
              banner-700.jpg 700w,
              banner-1200.jpg 1200w,
              banner-1600.jpg 1600w,
              banner-2000.jpg 2000w"
      src="banner-default-fallback.jpg"
      alt="Описание баннера">
</picture>

В этом примере мы с помощью атрибута sizes указали браузеру, сколько места будет занимать наше изображение на странице. В данном случае, если ширина области просмотра — 640px или больше, изображение будет занимать 80% от ширины области, и 100% в противном случае.

После этого в атрибуте srcset мы предоставляем браузеру список изображений — все они являются одним и тем же изображением, но имеющим разные размеры. Исходя из размеров, определенных sizes, браузер выберет наиболее подходящее из этих изображений и отобразит его.

Если браузер не поддерживает srcset в <img>, он просто отобразит запасной вариант, указанный в атрибуте src. Подробное описание работы этого метода вы можете найти в этой статье на сайте «A List Apart».

Управление оформлением: Загрузка разных SVG на экранах разного размера

Элемент <source>, который мы используем, чтобы указать, какое изображение(-я) нам необходимо, имеет и другой атрибут: media. Этот атрибут дает нам ту же гибкость при изменении фоновых изображений, что и медиазапросы в CSS, позволяя нам объединять в пары источники изображений с условиями их отображения (медиазапросами) прямо в исходном коде.

Поскольку мы отдаем SVG-изображение, нам не нужно предоставлять несколько его версий для разных разрешений экрана. Благодаря неограниченно масштабируемой природе SVG оно отлично выглядит при любом разрешении. (Ура!)

Но если у нас есть SVG, которое мы отдаем для ПК – к примеру, широкое изображение в хедере, это изображение может занимать сотни килобайт. Отдавать такое большое изображение для маленьких экранов – не лучшая идея, если взглянуть на это с точки зрения производительности. Кроме того, возможно вы просто не хотите отдавать такое же изображение на экранах меньшего размера, и вам нужна его «обрезанная» версия. Не так давно я работала над проектом, в котором была поставлена именно такая задача. Мой клиент не только хотел иметь другие изображения на экранах меньшего размера, но и вся композиция имела размер более 100 КБ, что, безусловно, слишком много для мобильных устройств, поэтому мы отдавали обрезанные версии этого изображения.

В подобных случаях вы можете указать разные SVG для загрузки при разных медиаусловиях с помощью атрибута media элемента <source>. В атрибуте media вы указываете медиаусловия, по аналогии с медиазапросами в CSS.

<picture>
    <source
        media="(max-width: 640px)"
        srcset="header--small.svg"
        type="image/svg+xml">
    <source
        media="(max-width: 1024px)"
        srcset="header--medium.svg"
        type="image/svg+xml">
    <source
        srcset="header--full.svg"
        type="image/svg+xml">
 
    <img src="header--default-fallback.jpg" alt="Описание хедера..">
</picture>

Безусловно, вы можете указать и различные резервные изображения для разных разрешений и размеров, подобно примеру в предыдущем разделе. Я пропущу этот шаг в данном разделе ради краткости, но общую «картину» вы уловили. (Заметили каламбур?)

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

На самом деле, параметры, имеющиеся у элемента <picture>, позволяют реализовать практически любой сценарий. Данная статья в блоге dev.opera описывает множество практических примеров использования и предоставляет примеры кода, чтобы помочь вам начать использовать этот элемент.

Как видите, используя элемент <picture>, нам больше не нужно использовать JavaScript для обеспечения резервной поддержки и/или изменения изображения в зависимости от различных медиаусловий. Ну, почти…

Поддержка браузерами и полифиллинг

На момент написания этой статьи элемент <picture> имеет не лучшую поддержку браузерами, но дела идут на лад. Множество умных людей работает над реализацией поддержки <picture> в различных браузерах. Следите за таблицей совместимости на сайте CanIUse.com, чтобы оперативно отслеживать состояние браузерной поддержки в будущем.

В то же время, и до тех пор, пока поддержка браузерами не достигнет хорошего уровня, вы можете использовать полифилл на JavaScript для браузеров, не поддерживающих этот элемент. Так что да, на сегодняшний день нам необходим JavaScript, но написанный вами код останется не устареет и в будущем, и все, что от вас потребуется в дальнейшем, когда поддержка станет лучше – это удалить полифилл, и ваш код продолжит работать без него именно так, как и ожидалось. При использовании <img> вам пришлось бы сделать внести намного больше изменений, либо, в крайнем случае, продолжить использование JavaScript.

Полифилл Picturefill, созданный ребятами из Filament Group, это де-факто кроссбраузерная поддержка <picture> уже сегодня. На странице этого полифилла представлена подробная документация по его использованию и советы по использованию <picture>, а также общие примеры.

Для использования полифилла вам всего лишь необходимо подключить скрипт в head вашей страницы:

<script src="picturefill.js" async></script>

Атрибут async сообщает браузеру, что загрузку picturefill можно выполнить асинхронно, не дожидаясь окончания загрузки, чтобы продолжить загружать оставшуюся часть документа. Согласно документации Picturefill, в случае добавления этого атрибута вам нужно добавить одну строчку скрипта перед тегом <script>, чтобы более старые браузеры могли распознать элементы picture, если они обнаружат их на странице до окончания загрузки Picturefill.

<script>
    // Элемент picture HTML5 shiv
    document.createElement( "picture" );
</script>
<script src="picturefill.js" async></script>

Если вы уже знакомы с HTML5 Shiv, то вы знаете, для чего нужна эта строчка. На самом деле, если вы уже подключили свежую версию HTML5 Shiv (иногда идущую в комплекте с Modernizr), эта строчка вам, возможно, и не нужна, поскольку она уже включена туда.

Решение проблемы с IE9

Хотя большинство версий IE (даже более старые!) хорошо поддерживаются Picturefill, при работе с IE9 возникает небольшой конфликт, который необходимо обойти. Для поддержки IE9 вам нужно обернуть элементы source, расположенные внутри вашего тега picture, в элемент video. Сделать это вы можете с помощью условных комментариев. — с Домашней страницы Picturefill

Согласно документации, полифилл обеспечивает поддержку <picture> во всех браузерах, но в IE9 ваши элементы source нужно оборачивать в тег video. И поскольку этот фикс необходим только в IE9, вы можете поместить его в условные комментарии, определяющие IE9:

<picture>
<!--[if IE 9]>

Использование SVG в изображениях в контенте для создания интерактивности и  управления стилями

Как уже упоминалось в начале этой статьи, элемент <img> и, конечно же, элемент <picture> позволяют вам загружать только статичные SVG-изображения либо SVG с анимацией, заданной внутри них. Если вам нужно загрузить изображение в контенте, и при этом вы хотите иметь возможность настраивать его стили и добавить ему интерактивность, используйте один из четырех доступных способов: <object><iframe>, <embed> и встроенные <svg>.

И <iframe>, и <object> по умолчанию имеют механизм использования запасных вариантов.

<object data="image.svg" type="image/svg+xml">
    <!-- Здесь запасной вариант -->
</object>
 
<iframe src="image.svg">
    <!-- Здесь запасной вариант -->
</iframe>

Встроенные <svg> требуют немного иных подходов для обеспечения запасных вариантов; один из таких подходов использует элемент <foreignObject>. Вы можете получить всю необходимую информацию о нем здесь. Кроме того, Крис написал о резервной поддержке SVG здесь.

 

Заключение

Хотя на сегодняшний день использование <picture> требует добавления полифилла на JavaScript, использование стандартной разметки HTML5 и достижение той свободы действий, что позволяет переключаться между изображениями с помощью родных элементов, даёт нам огромные возможности. А для подключения полифилла нужно всего лишь 1) скачать его, 2) добавить скрипт на страницу, 3) и всё. Это несомненно стоит того, если вы управляете оформлением страницы или обеспечиваете резервную поддержку для нескольких SVG-изображений в контенте.

Скорее всего, <picture> станет стандартным способом управления SVG и обеспечения запасных вариантов для img в будущем, но зачем начинать пользоваться им сегодня? И метод, использующий img, и новый элемент  picture требуют немного JavaScript’а — пока, но последний гораздо аккуратней и со временем не потеряет актуальности. Да, img тоже не потеряет актуальности, но используя picture вы в определенный момент сможете выбросить полифилл и оставить код без изменений, тогда как в случае с img вам придется либо и дальше использовать JavaScript, либо провести рефакторинг своей разметки, чтобы перейти к использованию picture без JavaScript.

Неважно, решите ли вы использовать <picture> для SVG сегодня или нет, с ним определенно стоит познакомиться поближе и использовать его для отдачи отзывчивых изображений в других форматах. Поэтому перед вами полный список статей, которые помогут вам влиться:

Рекомендованные статьи о <picture>

P.S. Это тоже может быть интересно:

3 Комментарии

  1. crazyyy

    При шаринге статьи не подгружает опенграфом название статьи, описание и картинку. неудобно

    1. Administrator

      Это для всех социальных сервисов или для каких-то определенных?

      1. Вадим Макеев

        Просто добавьте статье нормальный <title>, сейчас он общий для всего сайта (а ниже в комментарии идёт правильный)

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

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