21 мая 2011 в 16:02

Спрайтовая анимация на CSS 3

CSS*
Спрайтовая анимация — одна из тех вещей, которые при всей своей примитивности успешно работают и применяются в компьютерной графике и играх уже больше четверти века. Даже в трехмерных играх есть спрайты — например, билборды взрывов. Во многих браузерных и флеш-играх применяют именно спрайтовую анимацию, так как она очень проста и не требует высокой производительности — просто переключай кадры и все! А с появлением анимации в CSS 3 стало возможным использовать спрайты на своих страницах без яваскриптов.

Сразу хочу оговориться, что нижеописанный способ работает пока что только для webkit-браузеров.
Итак, возьмем простой спрайт из трех кадров:

Все, что нам надо с ним сделать, это поставить фоном в div и менять у него со временем background-position. Вроде бы все просто:
Copy Source | Copy HTML
  1. .sprite
  2. {
  3.     position: absolute;
  4.  
  5.     left: 50%; /* положение спрайта */
  6.     top: 33%;
  7.     width: 32px; /* его размер */
  8.     height: 32px;
  9.  
  10.     margin: -16px 0 0 -16px; /* просто чтобы он был по центру :-) */
  11.  
  12.     background: url(sprite.png) no-repeat 0 0; /* фон */
  13.  
  14.     -webkit-animation-name: sprite; /* название анимации */
  15.     -webkit-animation-duration: .3s; /* интервал в 300 миллисекунд */
  16.     -webkit-animation-iteration-count: infinite; /* повторять бесконечно */
  17.     -webkit-animation-timing-function: linear; /* использовать линейную функцию */
  18. }
  19.  
  20. /* анимация sprite */
  21. @-webkit-keyframes sprite
  22. {
  23.     /* перемещаем фон спрайта три раза, на четвертый возвращаем обратно в 0 */
  24.     0%
  25.     {
  26.         background-position: -0px 0;
  27.     }
  28.     33%
  29.     {
  30.         background-position: -32px 0;
  31.     }
  32.     66%
  33.     {
  34.         background-position: -64px 0;
  35.     }
  36.     100%
  37.     {
  38.         background-position: -0px 0;
  39.     }
  40. }

Но увы! В результате мы видим лишь одну порнографию. Картинка движется плавно, а не рывками длиной в кадр. Попробуем улучшить ситуацию, делая длинные интервалы и быстро переключая кадры:
Copy Source | Copy HTML
  1.  
  2. @-webkit-keyframes sprite
  3. {
  4.     0%
  5.     {
  6.         background-position: -0px 0;
  7.     }
  8.     33.332%
  9.     {
  10.         background-position: -0px 0;
  11.     }
  12.     33.334%
  13.     {
  14.         background-position: -32px 0;
  15.     }
  16.     66.665%
  17.     {
  18.         background-position: -32px 0;
  19.     }
  20.     66.667%
  21.     {
  22.         background-position: -64px 0;
  23.     }
  24.     99.999%
  25.     {
  26.         background-position: -64px 0;
  27.     }
  28.     100%
  29.     {
  30.         background-position: -0px 0;
  31.     }
  32. }
  33.  

Код получился довольно объемный, но результат нас почти устраивает, если бы не одно «но»: при маленьких интервалах можно наблюдать рывки.
Для полностью правильной анимации надо прибегнуть к другой хитрой возможности CSS 3: увеличению размера. Для этого установим ширину и высоту кадра в 1 пиксель и при помощи свойства transform увеличим спрайт до наших 32 пикселей. Так как transform влияет не только на размер самого элемента, но и на его фон, его размер установим в 3 пикселя по ширине и 1 по высоте, и в самой анимации сдвигать будем так же по 1 пикселю. Вместе с увеличением оно как раз и будет равно 32 пикселям.
Copy Source | Copy HTML
  1.  
  2. .sprite
  3. {
  4.     position: absolute;
  5.  
  6.     left: 50%;
  7.     top: 33%;
  8.     width: 1px; /* размеры элемента ставим в 1 пиксель */
  9.     height: 1px;
  10.  
  11.     margin: -16px 0 0 -16px; /* на отступы увеличение через transform не влияет */
  12.  
  13.     background: url(sprite.png) no-repeat 0 0;
  14.     background-size: 3px 1px; /* размер фона также уменьшаем */
  15.  
  16.     -webkit-animation-name: sprite;
  17.     -webkit-animation-duration: .3s;
  18.     -webkit-animation-iteration-count: infinite;
  19.     -webkit-animation-timing-function: linear;
  20.     -webkit-transform: scaleX(32) scaleY(32); /*  увеличим размер элемента */
  21.     -webkit-transform-origin: top left;
  22. }
  23.  
  24. @-webkit-keyframes sprite
  25. {
  26.     /* сдвигаем фон по 1 пикселю */
  27.     0.000%
  28.     {
  29.         background-position: -0px 0;
  30.     }
  31.     25.000%
  32.     {
  33.         background-position: -1px 0;
  34.     }
  35.     50.000%
  36.     {
  37.         background-position: -2px 0;
  38.     }
  39.     /* честно говоря, сам не понял, зачем надо сдвигать */
  40.     /* еще на один пиксель (ведь это должен быть */
  41.     /* пустой кадр). но иначе оно не покажет все */
  42.     /* кадры анимации. */
  43.     75.000%
  44.     {
  45.         background-position: -3px 0;
  46.     }
  47.     100.000%
  48.     {
  49.         background-position: -0px 0;
  50.     }
  51. }
  52.  

Теперь результат полностью соответствует нашим ожиданиям. Анимация нормальная, без рывков и прочего.
Для генерации ключевых кадров можно воспользоваться этой функцией:
Copy Source | Copy HTML
  1. function generateKeyframes($count, $sprite_width)
  2. {
  3.     $result = '';
  4.     $count++;
  5.     for ($i =  0; $i <= $count; $i++)
  6.     {
  7.         $result .= sprintf("\t%.3f%%\n\t{\n\t\tbackground-position: -%dpx 0;\n\t}\n", $i * 100. 0 / $count, ($i % $count) * $sprite_width);
  8.     }
  9.     return $result;
  10. }

Увы, этот способ пока что малоприменим на практике, так как работает только в Webkit-браузерах типа Chrome или Safari. В Firefox анимация так и останется плавной, в Opera показывается только первый кадр, а в IE оно вообще не работает. Так что анимация с использованием Javascript и Flash будет держать свои позиции еще довольно долго.
ertaquo @ertaquo
карма
109,2
рейтинг 0,5
Похожие публикации
Самое читаемое Разработка

Комментарии (24)

  • +1
    А можно поподробнее, почему сработал трюк с увеличением, а анимация с исходными размерами работала с ошибками?
    • +1
      Потому что увеличение через transform влияет только на размеры. Это все равно, что лупу приложить. У элемента ширина 1 пиксель, но он отображается с растяжкой на ширину кадра. Так что в данном случае при анимации смещение идет на 1 пиксель, а не на 32 пикселя. Ну а дальше непонятно, какое поведение считать правильным: Webkit или Gecko. В первом скорее всего идет округление до целых пикселей, а во втором используются дробные значения, поэтому в Webkit анимация отображается нужными нам рывками, а в Gecko все так же сглаженно.
  • +4
    Я думаю анимация при помощи JS/Flash будет держать свои позиции даже после того как все браузеры станут поддерживать такую фичу. Потому что способ описанный вами представляет интерес только как «посмотрите, а вот мы и так умеем!», т.е. чисто академический интерес. На практике он совершенно не очевиден и плохо сопровождаем.
    • +1
      Конечно. Для кроссбраузерной подобной анимации в яваскрипте надо написать три строчки, а в css получается на два десятка строк и нифига не кроссбраузерно.
      • 0
        Пока это только попытки реализовать стандарт, который ещё не утвержден (думаю и не скоро будет)

        А помимо css сейчас активно продвигается svg и скажем это тоже конкурент!

        и если честно Flash/js/css/svg они хороши для разных задачь! Простую анимацию адекватный человек не будет делать на флеше, а красивую игру сейчас объективно лучше делать на флеш и тд.
  • +7
    Ctrl + + превращают это в кошмар.
    • +1
      Потому что в backrgound-position значения задавать надо было не в пикселях, а в процентах — вы ведь об этом?
    • 0
      Ctrl — - прикольнее :)
    • 0
      Кстати, мне совершенно непонятно такое поведение. Если получать координаты курсора мыши, то они — мастабируются вместе с масштабом браузера. т.е., например, я увеличиваю всю страницу, а глаз как был 25-80, так и остался.
      • 0
        Дело в том, что элемент до этого уже был отмасштабирован так, чтобы его визуальные размеры были равны размену кадра. Но при масштабировании всей страницы целиком масштаб элемента также изменится и не будет соответствовать размеру кадра.
  • 0
    > В Firefox анимация так и останется плавной

    щито? У меня как и в опере 1ый кадр. Откуда в фф анимация?
    • 0
      Если заменить префикс "-webkit-" на "-moz-", то будет анимация.
      • +1
        + использовать нестабильную версию вроде Firefox Aurora 5 и т.п., ибо в текущей 4-ой версии css3 animation отсутствует.
  • +5
    ссылка «порнография» разочаровывает :)
  • 0
    а cpu как жрет-то :)
    • 0
      2-3% в хроме на маке
      • 0
        10 в хроме на q6600
  • +1
    Мне кажется, спрайты нужно оставить для разработчиков игр и флеш-приложений. На веб-сайтах гораздо целесообразнее использовать обычную анимацию, будь то apng или gif. При меньших усилиях и размерах мы получим всё тот же результат. Apng, правда, далеко не кроссбраузерный, но, будем надеяться, это временно.
    • 0
      да скорее svg, его щас начинают подключать и возможности хорошие, и простой удобный js никто не отменял.
  • +1
    Для меня ключевым стал последний абзац.
  • 0
    извините за оффтоп, но зачем вы мучаете php интерпретатор?
  • 0
    Налицо нецелевое использование ресурсов процессора. Нужно перерисовать один кадр 3 раза в секунду, а вы заставляете его делать ненужные вычисления, прорисовки и т.д.

    Но повторю вышесказанное, с академической точки зрения подход имеет смысл в качестве демонстрации возможностей. Ну и развлечения ради.
    • 0
      С помощью этой технологии можно создавать более качественную анимацию, чем это может предложить GIF-формат
  • 0
    Видел нечто похожее у Вадима Макеева (http://pepelsbey.net/2010/12/darkbox-return/).

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.