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

    Спрайтовая анимация — одна из тех вещей, которые при всей своей примитивности успешно работают и применяются в компьютерной графике и играх уже больше четверти века. Даже в трехмерных играх есть спрайты — например, билборды взрывов. Во многих браузерных и флеш-играх применяют именно спрайтовую анимацию, так как она очень проста и не требует высокой производительности — просто переключай кадры и все! А с появлением анимации в 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 будет держать свои позиции еще довольно долго.
    Метки:
    Поделиться публикацией
    Похожие публикации
    Комментарии 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 как жрет-то :)
    • +1
      Мне кажется, спрайты нужно оставить для разработчиков игр и флеш-приложений. На веб-сайтах гораздо целесообразнее использовать обычную анимацию, будь то apng или gif. При меньших усилиях и размерах мы получим всё тот же результат. Apng, правда, далеко не кроссбраузерный, но, будем надеяться, это временно.
      • 0
        да скорее svg, его щас начинают подключать и возможности хорошие, и простой удобный js никто не отменял.
    • +1
      Для меня ключевым стал последний абзац.
    • 0
      извините за оффтоп, но зачем вы мучаете php интерпретатор?
    • 0
      Налицо нецелевое использование ресурсов процессора. Нужно перерисовать один кадр 3 раза в секунду, а вы заставляете его делать ненужные вычисления, прорисовки и т.д.

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

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