Страницы

вторник, 11 июня 2013 г.

Верстаем иконки под планшеты/мобильные устройства

Сложность верстки под планшеты и мобильные устройства не только в том, чтобы сделать responsive веб-дизайн, что само по себе большой труд и дизайнера, и верстальщика. Суть также в том, что кроме огромного набора размеров экрана мы еще получаем и разное количество пикселей на дюйм, именуемое как device-pixel ratio (тут можно подробнее почитать на англ.). Беда в том, что это соотношение не стандартизировано и в итоге у нас получается что-то вроде:

-webkit-min-device-pixel-ratio Устройство
1.0 All non-Retina Macs
All non-Retina iOS devices
Acer Iconia A500
Samsung Galaxy Tab 10.1
Samsung Galaxy S
1.3 Google Nexus 7
1.5 Google Nexus S
Samsung Galaxy S II
HTC Desire
HTC Desire HD
HTC Incredible S
HTC Velocity
HTC Sensation
2.0 iPhone 4
iPhone 4S
iPhone 5
iPad (3rd generation)
iPad 4
All Macs with Retina displays
Google Galaxy Nexus
Google Nexus 4
Google Nexus 10
Samsung Galaxy S III
Samsung Galaxy Note II
Sony Xperia S
HTC One X
3.0 HTC ButterflySony Xperia S

А это чревато тем, что картинки, рассчитанные на стандартное разрешение, будут смотреться "размытыми" и пиксельными.

1. Одна большая картинка

Самое простое решение состоит в том, чтобы сделать картинку самого большего размера, то есть с device-pixel ratio = 3 и масштабировать при отображении в заданную область. Но в этом случае при просмотре на мониторе (device-pixel ratio = 1) мы также будем грузить картинку в 3 раза большую, чем нам необходимо.

2. Картинки разных размеров

Другой способ позволяет загружать картинку в соответствии с разрешением экрана. Для этого мы добавляем картинки соответствующего размера (т.о. к одной logo.png добавляются logo@1.25x.png, logo@1.3x.png, logo@1.5x.png, logo@2x.png и logo@3x.png) и вводим дополнительные css-свойства на проверку -webkit-min-device-pixel-ratio и min-resolution (подробнее об этих свойствах на англ).

В результате получаем:

.logo {
  background-image: url('logo.png');
  background-size: cover;
  height: 30px;
  width: 60px;
}

@media 
(-webkit-min-device-pixel-ratio: 1.25), 
(min-resolution: 120dpi){ 
  .logo {
    background-image: url('logo@1.25x.png');
  }
}

@media 
(-webkit-min-device-pixel-ratio: 1.3), 
(min-resolution: 124.8dpi){ 
  .logo {
    background-image: url('logo@1.3x.png');
  }
}

@media 
(-webkit-min-device-pixel-ratio: 1.5), 
(min-resolution: 144dpi){ 
  .logo {
    background-image: url('logo@1.5x.png');
  }
}

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

@media 
(-webkit-min-device-pixel-ratio: 3) { 
  .logo {
    background-image: url('logo@3x.png');
  }
}

Довольно большие конструкции, и как вариант можно было бы ограничиться двумя выборками: с коэффициентов = 1.5 и 3.0, совместив с первым методом растяжения картинки на всю ширину. Это было бы актуально для Kindle Fire HD 8.9, у которого device pixel ratio = 3.75, а значит он не попадал бы в вышеуказанные условия.

3. SVG

Более изящным и современным решением было бы использование SVG (от англ. Scalable Vector Graphics — масштабируемая векторная графика), который на данный момент поддерживается чуть ли не всеми браузерами (мы практически избавлены от геммороя прошлых лет), но если нужна поддержка Всех, то для них необходимо дополнительно хранить картинку в *.png.

Интересный метод предложил Peter Gasston. Он (метод) основан на использовании множественного бэкграунда и идеи, что если браузер не поддерживает это свойство, он не поддерживает и svg формат. Выглядит примерно так:
p {
  background-image:  url('image.png');
  background-image:  none,url('image.svg'), url('image.png');
  background-size: 100% 100%;
}
Но к сожалению в основном такая неподдержка есть в ие8 и ниже, а так же Android 2.3, для для андроида этот способ никак не спасает, потому что он поддерживает множественное фоновое картинкообразование, но не поддерживает svg ((
Поэтому для динозавра ие я предпочитаю добавленный *.png показывать с помощью хака "\9", который распознается ie <= 8. Это можно удобно оформить в отдельный миксин на sass:

@mixin image_svg($url, $options) {
  background: image-url($url + '.svg') $options;
  background: image-url($url + '.png') $options \9;
}

p {
  @include image_svg("logo", 10px 5px no-repeat);
}

А вот с Android 2.3 все-таки будем в пролете =/
Чтобы совсем уже красиво замещать svg на png, можно при загрузке страницы делать проверку и добавлять класс для body:

if (!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1")) {
  $('body').addClass('no-svg');
}
А в стилях выводить нужные урлы на иконки:

p {
  background-image:  url('image.svg');
}
.no-svg p {
  background-image:  url('image.png');
}

Perfect!

Один момент, в котором svg может проигрывать png - это размер файла. Но и тут легко быть на коне с помощью svgz, который сжимает svg утилитой Gzip до приемлемого  размера.

4. @font-face

Безусловно, на этом можно было бы и остановиться, но что если на сайте есть несколько небольших монохромных иконок, которые можно было бы засунуть в какой-нибудь один удобный спрайт? Что если использовать свой шрифт, в котором будут все необходимые иконки? Суть в том, что каждая иконка представлена определенной буквой шрифта, т.о. она получает свой векторный эквивалент и может масшабироваться как шрифт и иметь те же самые css-свойства, что и у текста.

Делается это за 5 минут:

С помощью шрифтобелки (или любой другой подобной штуки) генерируем из исходного шрифта myfont-webfont.ttf еще несколько форматов:
  • myfont-webfont.eot
  • myfont-webfont.svg
  • myfont-webfont.woff
Далее подключаем их через @font-face и используем:
@font-face {
  font-family: 'myfont-webfont';
  src: url('/font/myfont-webfont.eot');
  src: local('/font/myfont-webfont'),
       url('/font/myfont-webfont.woff') format('woff'),
       url('/font/myfont-webfont.ttf') format('truetype'),
       url('/font/myfont-webfont.svg#webfont') format('svg');
}

body {
  font-family: myfont-webfont, Times New Roman;
}

Вуаля! =)

Если сет определенных иконок используется в нескольких проектах, имеет смысл сделать его гемом.

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

5. custom font

К счастью, есть замечательный генератор Font Custom, который делает за нас всю грязную работу, а именно: одной командной строкой перефигачивает все svg в один большой и могучий шрифт, на лету создавая различные вариации! Т.о. не нужно заботиться о шрифте, нужно просто складывать все svg в одну папку.

Что нужно для этой далеко не уличной магии?
Если вы работаете на Mac OS, необходимо присутствие xcode. (Под Линукс данная магия тоже работает, но мной не опробована). Далее ставим fontforge и сам гем FontCustom v1.0.0:
brew install fontforge eot-utils ttfautohint
gem install fontcustom
После создаем пути к папке, где будут храниться наши svg файлы, и к папке, в которую сгенерируется шрифт. Все. Следующая строчка выполняет преобразования и наш шрифт готов:
fontcustom watch app/assets/images/svg -o app/assets/stylesheets/iconfont -t scss

Теперь при добавлении еще одной svg-иконки нам нужно будет только заново запустить эту строку, которую возможно удобно было бы оформить в rake task.

Для рельсового проекта также добавляем в Gemfile (не забыв про последующий: bundle install):
gem 'fontcustom'
А в css делаем импорт стилей:
@import "_fontcustom";
Использование очень похоже на бутстраповское: для добавления к элементу иконки добавляем нужный класс.
<a class="icon-menu" href="#">item</a>
где в имени класса "icon-" - это префикс, а "menu" - это название svg-файла.

Мне безумно нравится этот вариант, но хотелось бы знать: оправдано ли использование в каком-то конкретном случае, или все же лучше подгружать svg?

Статистика по @font-face:

Немного статистики. Для примера я взяла три небольшие иконки со своими особенностями:

  1. Пиксельная иконка, для которой характерны четкие линии в 1 пиксель.
    1x: ; 1.5x: ; 3x:
    SVG представление:
    <svg enable-background="new 0 0 15 19" height="19px" id="Layer_1" version="1.1"
    viewbox="0 0 15 19" width="15px" x="0px" y="0px" xml:space="preserve"
    xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
    <g>
      <rect height="1" width="15" x="0" y="0">
      <rect height="1" width="15" x="0" y="18">
      <rect height="19" width="1" x="0" y="0">
      <rect height="19" width="1" x="14" y="0">
    
      <rect height="4" width="7" x="4" y="3">
    
      <rect height="1" width="7" x="4" y="10">
     <rect height="1" width="7" x="4" y="12">
     <rect height="1" width="7" x="4" y="14">
    </rect></rect></rect></rect></rect></rect></rect></rect></g>
    </svg>
  2. Иконка паучка.
    1x: ; 1.5x: ; 3x:
    Исходный код SVG взят здесь.
  3. Иконка состоит из двух палочек, одна из которых имеет прозрачность 0.6:
    1x: ; 1.5x: ; 3x:
    SVG код:
    <svg enable-background="new 0 0 12 14" height="14px" id="Layer_1" version="1.1"
    viewbox="0 0 12 14" width="12px" x="0px" y="0px" xml:space="preserve" 
    xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
    <g>
     <rect height="14" width="5">
     <rect height="14" opacity="0.6" width="5" x="7">
    </rect></rect></g>
    </svg>

Посмотрим как они будут рендериться разными браузерами (масштаб 4:1, чтобы не напрягать глаза):


Как видно, полупрозрачность игнорируется в иконко-шрифте, чего и следовало ожидать. Паучок выглядит отлично, несмотря на то, что в Firefox'е смотрится немного жирнее, но в целом на отлично. А вот пиксельная графика абсолютно не подходит для отображения в виде шрифта, потому что только IE10 смог корректно отбразить без лишнего сглаживания.

Т.о. можно сделать выводы какими должны быть иконки для того, чтобы использование иконко-шрифта оправдывало себя. (Здесь идет самостоятельная работа).

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

svg, bytes ratio = 1, bytes ratio = 1.5, bytes ratio = 3, bytes
(15x19) 755 162 394 184
(15x21) 7,493 379 518 686
(12x14) 476 135 142 149
Общий размер 8,724 822 1,054 1,019

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

имя файла размер
_fontcustom.scss 955 bytes
fontcustom.eot 2,692 bytes
fontcustom.svg 7,164 bytes
fontcustom.ttf 4,996 bytes
fontcustom.woff 1,672 bytes

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

Но прежде чем использовать, хотелось бы подумать еще вот о чем: мы засовываем в иконко-шрифт все svg-картинки, а их может быть и 30 и 50, в то время как на одной странице нам могут понадобиться только три из них, а на другой всего одна, но шрифт уже загружен. И тут важно подумать как часто пользователь будет смотреть на страницу с 1 иконкой, с тремя и с 30-ю.

На мой взгляд, именно из-за уникальности каждого проекта (и отсутствия панацейи), стоит выбирать те методы, которые лучше подходят. Для начала ответить на ряд вопросов:
  • Как много графики в проекте?
  • Требуется ли поддержка ie8 или меньше? или android 3.0 или меньше?
  • Есть ли одноцветные небольшие иконки? Как много?
  • Есть ли пикельно-зависимые иконки?

P.S. И на последок: статья об использовании иконок в стилях, но что если мы хотим применить responsive web-design для картинок, подключаемых непосредственно в html? Тут тоже есть свои методы.