Bootstrap CSS Sprite: синтаксический сахар для <img />

    UPD


    Сегодня в этом уже нет никакого смысла. Просто настройте себе HTTP/2



    Что это?


    В один прекрасный день я отчётливо понял, что устал писать длинные ссылки на файлы изображений, каждый раз задавать им ширину и высоту, заботиться о том, чтобы это всё не прыгало при загрузке и не мигало при наведении мышкой. И я решил автоматизировать всю эту рутину. Так появился Bootstrap CSS Sprite — библиотека, которая позволяет работать со всеми вашими изображениями, как с одним спрайтом. При этом доступ к тайлам спрайта осуществляется в стиле Twitter Bootstrap.

    Приведу пример: у нас есть файл изображения cat.png. Чтобы показать это изображение надо использовать тег <i>, указав для него CSS-класс img-cat, как мы делаем это в Twitter Bootstrap:

    <i class="img-cat"></i>
    



    Преимущества


    • Вместо множества файлов с изображениями мы получаем один и, как следствие, всего один запрос к серверу вместо множества.
    • Смена изображения при первом наведении мышкой (hover) без мигания и подёргиваний.
    • Больше нет нужды указывать ширину и высоту для каждого изображения в HTML-шаблонах — библиотека сделает это за нас в сгенерированном CSS-файле.
    • Меньше HTML-кода:
      <i class="img-cat"></i>
      
      вместо
      <img src="<?=$this->theme->baseUrl?>/images/cat.png" style="width: 64px; height: 64px;" />
      

      Думаю, это будет действительно экономить время!


    Как использовать


    Давайте рассмотрим самый простой способ использования библиотеки. Для генерации спрайта надо указать, где и какие файлы надо брать. В нашем случае берём jpg, jpeg, gif и png из директории /path/to/images/source. Затем указываем путь к файлу спрайта — /path/to/images/sprite.png — в него будут объединены все исходные изображения. Также будет сгенерирован CSS-файл — /path/to/css/sprite.css, который содержит классы для всех обработанных изображений. Эти классы определяют ссылку на изображение, его размеры, а также поведение при наведении мышкой.
    $sprite = new BootstrapCssSprite(array(
        'imgSourcePath' => '/path/to/images/source',
        'imgSourceExt'  => 'jpg,jpeg,gif,png',
        'imgDestPath'   => '/path/to/images/sprite.png',
        'cssPath'       => '/path/to/css/sprite.css',
        'cssImgUrl'     => '/url/to/images/sprite.png',
    ));
    $sprite->generate();
    
    Генерацию надо запускать только при добавлении новых изображений.

    Также уже готова реализация в виде компонента для Yii Framework. Работа с ним будет полностью аналогична. Надо просто скопировать файл YiiBootstrapCssSprite.php в /extensions/ и добавить его в конфиг:
    'components' => array(
        ...
        'sprite' => array(
            'class'         => 'ext.YiiBootstrapCssSprite',
            'imgSourcePath' => '/path/to/images/source',
            'imgSourceExt'  => 'jpg,jpeg,gif,png',
            'imgDestPath'   => '/path/to/images/sprite.png',
            'cssPath'       => '/path/to/css/sprite.css',
            'cssImgUrl'     => '/url/to/images/sprite.png',
        ),
        ...
    )
    
    После чего генерируем спрайт:
    abstract class BaseController
    {
        public function init()
        {
            ...
            if (APP_ENV === APP_ENV_DEV) {
                Yii::app()->sprite->generate(); // Regenerates sprite only if source dir was changed
            }
            ...
        }
    }
    


    :hover


    Для того, чтобы менять картинку при наведении мышкой, достаточно всего лишь положить файл изображения cat.hover.png рядом с cat.png. И это всё! Если же надо изменять картинку при наведении мышкой на её родительский элемент (а не на саму картинку), тогда придётся попотеть и добавить CSS-класс hover-img к этому элементу:
    <button class="btn hover-img"><i class="img-cat"></i> My Cat</button>
    

    Также есть возможность вручную эмулировать событие наведения:
    $('.img-cat').addClass('hover');
    


    Планы на будущее


    Планов по развитию довольно много. Из основного:
    • Реализовать генерацию спрайта с использованием разных графических библиотек (сейчас есть реализация только с GD lib).
    • Оформить библиотеку в виде компонента (бандла, etc.) для основных PHP-фреймворков (Yii, Zend, Symfony).
    • Портировать библиотеку на другие веб-ориентированные языки (Ruby, Python, .Net, Java, etc.).

    Надеюсь, что после публикации статьи, планы на будущее значительно расширятся, а радость веб-разработчиков всех стран и народов повысится. Жду ваших комментариев и пулл реквестов :)

    UPD maximw высказал хорошую идею: заменить тег <i> на <div>. Можно будет сделать этот тег конфигурируемой опцией. А @SelenIT2 дал очень дельное уточнение о том, что заменить лучше не на <div>, а на <span>, дабы не порвать DOM при вставке в строчные элементы.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 62

      +27
      Я никак не могу понять тенденцию использовать тэги не по назначению. Тэг <i> — выделение текста курсивом, какого черта делать из этого картинку.

      Было такое, что я изменял чужой код. Для меня пустой тэг <i> — это отсутствующий текст курсивом. Я его удалил как мусор, т.к. текста нет и в той области сайта нет генерируемого в JS контента. А оказалось там были какие-то иконки. Пришлось исправлять назад.

      Пожалуйста, не делайте геморрой при поддержке кода. Используйте тэги по назначению.
        –11
        Данный подход я позаимствовал у далеко не последнего по популярности фреймворка. И да, тег <i> не пустой, у него есть класс img-cat, что как бы намекает. Порой, выйти за рамки — это хорошо :)
          +14
          Я понимаю, что я «никто и ничто» чтоб критиковать Twitter Bootstrap, тем не менее, даже у них — это плохая практика. Имхо.

          А чем плох <span class=«img-cat»> или <div class=«img-cat»>. Длиннее на 2-3 символа? Зато понятно что это какой-то блок, а не текст курсивом.
            –4
            У <img /> есть обязательный атрибут src, который сводит всю задумку на нет. Я сам бы рад использовать именно этот тег.
              +2
              и alt тоже обязательный, кажется
                +1
                Да, точно.
                  +1
                  alt\title помогает гуглу индексировать картинки
                    +1
                    Либа предназначена для оформительских картинок (иконок например). Она категорически не подходит для отображения целевых картинок (например фотографий на Фликре). Поэтому их ценность для гугла стремится к нулю или даже уходит в минуса.
                  0
                  Можно написать сниппет для вашего текстового редактора, например
                  testimg-800-600
                  который по хоткею вставит что-нибудь такое:
                  <img src="http://placekitten.com/800/600" width="800" height="600" alt="Test image">
                  Выход может быть в любом синтаксисе (haml если надо)

                  Или я не совсем понял проблемы?
                    +1
                    Помимо выигрыша в HTML-коде, тут идёт использование одного спрайта для всех картинок, и очень удобная поддержка :hover
                    –1
                    Никто не запрещает оставлять значение src пустым (вуаля, src="", и всё замечательно).
                      +2
                      Это будет невалидно. Если идти в этом направлении, тогда уже надо писать либо src="//:0", либо src="about:blank".
                        +2
                        Извиняюсь, был неправ.
                        • UFO just landed and posted this here
                        • UFO just landed and posted this here
                            +4
                            Извиняюсь, был неправ.
                          0
                          Пихните в него пустую прозрачную гифку 1х1 пиксель и проблема решена. В погоне за непонятой экономии символов получается невероятная чушь, по сути можно всю html верстку сделать из тега i (+ огромная таблица стилей, перепределяющая поведение тега в заивисимости отустановленного класса) и будет экономно и черт ногу сломит что с этим делать. Полностью согласен что теги надо использовать по назначению.
                          Меньше HTML-кода:
                          <i class="img-cat"></i>

                          вместо
                          <img src="<?=$this->theme->baseUrl?>/images/cat.png" style="width: 64px; height: 64px;" />

                          Думаю, это будет действительно экономить время!


                          вы забыли указать что весь этот длинный код просто ушел в css файлик
                            +1
                            Вы указали всего лишь один пункт из 4. И при этом довели его до абсурда.
                              0
                              Ну незнаю, имхо абсурд это использование тега курсива для вывода бекграунда чтобы имитировать картинку. Ну да бог с ним, кому как нравится, просто потом тяжело разгребать все эти хитросплетения верстальщиков, когда шаблон на проект натягиваешь и мозг рушится от нелогичных решений, которые еще надо заставить работать, да к ним еще и IE хаки под каждый недобраузер применить.
                              Можно кстаии вообще свой тег сделать, типа и ему в css-ки прописать:
                              mycat {
                              width:
                              height:
                              background: url(tralalala)
                              }

                              Давно такое не пробовал, но помнится что в Опере лет 5 назад прокатывало — делал свои теги для отображения модулей внутри WYSIWYG редатора.

                              Про пункты… 1 пункт уже давно многими для веба используется (у гугла вроде как 1 спрайт на всё), конечно всё подряд в спрайты не напихать, но например хорошо подходит для круговых обзоров товаров (не помню как это правильно называется, когда мышкой крутишь, а объект как бы крутится по одной или 2м осям, т.е. просто огромный бэкграунд передвигается), 2 пункт вытекает из первого, 3 пункт — раздутая css-ка или гора css-ок под каждый шаблон (но это не в коем слуучае не минус, у самого такая же штука, да и gzip никто не отменял)
                                0
                                Тоже думал о том, чтобы ввести свой тег. Возможно сделаю это вообще настройкой, чтобы каждый мог указать по своему вкусу. Но изначально ориентировался на Twitter Bootstrap, как на очень популярное решение.
                                • UFO just landed and posted this here
                                    +1
                                    Кстате для таких не хитрых программистов как вы, нормальный хитрый верстальщик засунет в этот тег знак пробела чтоб вы видели его и подумали 10 раз перед тем как удалить его.
                              0
                              О, Вы немного подправили коммент. Думаю это хороший вариант, чем не повод для пулл реквеста?
                                0
                                Да, прошу прощения. Забыл, что парсер комментариев съедает тэги, поправил символы < >.
                                0
                                Полагаю, тег <i> в качестве жертвы был выбран из-за намеков на слова «icon» или «image». Кто-то курсив и тегом <em> делает
                                • UFO just landed and posted this here
                              –1
                              Для курсива уже используется em Для курсива i использовать уже не верно вроде бы как)
                                +1
                                Не совсем верно, он всё также в строю, просто теперь у него сузилась семантика.
                                  +1
                                  Спасибо, буду знать!
                                • UFO just landed and posted this here
                                    0
                                    Аналогично с тегами <b> и <strong>, первый делает физически жирным текст, а второй определяет важность помеченного текста, но браузеры отображают их одинаково. Напрашивается вопрос — так задумано на будущее, если вдруг решат что их можно отображать по разному или это для поисковиков так задумано?
                                    • UFO just landed and posted this here
                                  +1
                                  Вы в большей части бекенд программист?
                                    +1
                                    Да.
                                    Из чего вывод сделали, если не секрет?
                                      0
                                      Я фронтенд разработчик и сразу видно программиста когда он начинает рассуждать о тегах, css и подобных вещах. И часто видно что им встречается работа только плохих фронтенд разработчиков.
                                        0
                                        Если бы был фронтэнд разработчик — тэги и css были бы его головной болью. Я бы только контроеллеры-модельки пописывал. :)

                                        Но нас есть верстальщик, на нем верстка + немного js, слайдеры, окошки и т.п. Я думаю фронтэнд разработчик это гораздо более широкое понятие. А вот верстальщик, да, видно что делает лишь бы выглядело как на макете и во всех браузерах. Порой кажется, что он даже не понимает как этот код потом будет преобразован во view. И что надо делать чтоб он был удобен в использовании, максимально стойким к правкам, которые всегда есть и будут.
                                        Сейчас понемногу собираю материалы по верстке (хотя сам в ней довольно слаб) обязательные для ознакомления верстальщикам.
                                          0
                                          А почему бы верстальщику не делать сразу во вьюхах код? Потом будет гораздо проще заменять текстовую рыбу на переменные. Правда здесь надо очень хорошо понимать что и в каком виде придёт в шаблон.

                                          Я вообще против того, чтобы отдавать в интеграцию чистый html.

                                          По поводу материалов можем пообщаться отдельно, у меня тоже есть мысли на этот счёт, но с другой стороны баррикад.
                                  +1
                                  Я для генерации спрайтов использую glue, для автоматического расчёта размеров rails-sass-images
                                  Там всё красиво + retina

                                  Раньше compass'ом баловался, но он перестал быть актуальным чуть менее чем полностью

                                  Для тестовых картинок http://placekitten.com/ & http://lorempixel.com/
                                    0
                                    Хорошие инструменты, смотрел на них перед написанием либы. Но рассмотренная либа даёт ощутимо больше возможностей.
                                      0
                                      Можно уточнение в чем же потеряна актуальность компасса?
                                        +1
                                        Например, добрая половина миксинов заменяется этим https://github.com/ai/autoprefixer
                                          0
                                          Вы так говорите, будто Compass — это только ценный мех работа с префиксами.
                                            0
                                            А что там такого ещё есть выдающегося?
                                            Вполне хорошо живётся с http://bourbon.io/docs/ + compass для rails 4 нестабилен
                                    • UFO just landed and posted this here
                                        +1
                                        Очень дельное уточнение, спасибо!
                                        0
                                        Хм

                                        Прежде чем делать открытие — загляни в справочник.

                                        compass-style.org/help/tutorials/spriting/
                                          0
                                          мне тоже кажется что по сравнению с компасом это неочень…
                                            0
                                            Не видел у них поддержки :hover, плюс хотелось реализации именно в стиле Twitter Bootstrap.
                                          0
                                          Кстати, в 3-ем бутстрапе иконочки вынесены в отдельный репозиторий и тег <i> заменён на <span>
                                          http://glyphicons.getbootstrap.com/
                                            0
                                            Отлично! Предложение перейти на <span> уже звучало несколько раз. И я добавил апдейтом в пост, что планирую сделать это изменение.
                                              0
                                              То-есть из-за того что функция возвращает список тегов развернулся эпичный срач какой тег юзать? (я не нашел больше в коде нигде теги)
                                              Интересует простой китайский вопрос — а на хуа? Куда потом этот список тегов юзать, кроме как для примера?
                                              Ну и сразу чтобы два раза не вставать — а чем compass не в стиле бутстрапа, раз ховер и компанию уже нашли?
                                            +2
                                            Если картинка несет смысловое значение (относится к тесту) то следует использовать отдельные файлы изображений, тэг img с аттрибутом alt.

                                            Если же картинка играет роль иконки, бэкграунда и не несет смыслового значения, то лучше всего задавать ее в качестве background-image для нужного тега. (найдено на stackoverflow)

                                            При этом очистить html-код от мусорных тегов, вроде предложенных вами <i class="some-image"></i> или предложенных в комментириях <span class="some-image"></span> помогут псевдоэлементы ::before и ::after коих целых 2 на каждый тег (за исключением input)

                                            Генерация спрайтов — задача тривиальная и решенная уже много-много раз (полный список решений). Тем более, последнее время использование data:uri считается более лучшим, так как экономит память и количество запросов к серверу, и, в качестве бонуса, избавляет от головной боли слежения за версией спрайта (если сайт обновляется и с некоторой периодичностью в спрайт добавляются новые иконки).

                                            Так, собственно вопрос, зачем еще одно решение задаче, которая уже давно решена?
                                              0
                                              Если все ради того, чтобы ускорить набор кода, то я вам советую выбрать хороший редактор. Например: webstorm с плагином emmet, live templates и автоматической подстановкой (включая размеры картинок) по клавише tab
                                              • UFO just landed and posted this here
                                                  0
                                                  Не стал вдаваться в поддробности по поводу тегов без псевдоэлементов, спасибо за дополнение.
                                                  А по поводу иконок — опять таки, если иконка прямо относится к тексту, то используйте img, если нет — все что угодно, но при этом по возможности семантично (лишние теги плодить не имеет смысла).

                                                  И, кстати, забыл упомянуть, что если верстка проекта нацелена на хорошие браузеры (IE9+) то есть еще возможность использовать множественные бэкграунды, для избавления от лишних тегов.
                                                  0
                                                  data:uri бесспорно интересная технология, но в FF и IE работает далеко не оптимально. И этим никак нельзя принебречь. А за версией спрайта мне кажется следить очень просто: по времени его создания и времени обновления директории с исходными изображениями. Вот Вам и один из плюсов моего велосипеда :)
                                                    0
                                                    Для большей нагядности приведу пример кода из документации:
                                                    abstract class BaseController
                                                    {
                                                        public function init()
                                                        {
                                                            ...
                                                            if (APP_ENV === APP_ENV_DEV) {
                                                                Yii::app()->sprite->generate(); // Regenerates sprite only if source dir was changed
                                                            }
                                                            ...
                                                        }
                                                    }
                                                      +1
                                                      Склонен не доверять результатам данного теста, тесты какие-то слишком синтетические. Я ради эксперимента провел аналогичный сравнительный анализ на одном из последних своих проектов: два теста на двух сборках проекта — одна с картинками в файлах, другая с картинками в data:uridata:uri добавлялись только иконки, причем оптимизированные с помощью imageOptim, размер не более 2х кб на каждую). И результаты в FF (версии 21, 22) получились ровным счетом обратные, в сборке с data:uri событие window.onload срабатывало в среднем в 3-4 раза быстрее. Для чистоты эксперимента — в Сhrome и FF nightly 25.0a1 data:uri быстрее в 2 раза
                                                        +1
                                                        Посмотрел дату публикации той статьи и понял, что я неправ :)
                                                    0
                                                    Сегодня в этом уже нет никакого смысла. Просто настройте себе HTTP/2

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