20 KiB
CSS-анимации
Все современные браузеры, кроме IE9- поддерживают CSS transitions и CSS animations, которые позволяют реализовать анимацию средствами CSS, без привлечения JavaScript.
Однако, как мы увидим далее, для более тонкого контроля анимации JavaScript вовсе не будет лишним. [cut]
CSS transitions [#css-transition]
Идея проста. Мы указываем, что некоторое свойство будет анимироваться при помощи специальных CSS-правил. Далее, при изменении этого свойства, браузер сам обработает анимацию.
Например, CSS, представленный ниже, 3 секунды анимирует свойство background-color
.
.animated {
transition-property: background-color;
transition-duration: 3s;
}
Теперь любое изменение фонового цвета будет анимироваться в течение 3х секунд.
При клике на эту кнопку происходит анимация её фона:
<!--+ run autorun height=60 -->
<button id="color">Кликни меня</button>
<style>
#color {
transition-property: background-color;
transition-duration: 3s;
}
</style>
<script>
color.onclick = function() {
this.style.backgroundColor = 'red';
}
</script>
Есть всего 5 свойств, задающих анимацию:
- `transition-property`
- `transition-duration`
- `transition-timing-function`
- `transition-delay`
Далее мы изучим их все, пока лишь заметим, что общее свойство transition
может перечислять их все, в порядке: property duration timing-function delay
, а также задавать анимацию нескольких свойств сразу.
Например, при клике на эту кнопку анимируются одновременно цвет и размер шрифта:
<!--+ run height=80 autorun -->
<button id="growing">Кликни меня</button>
<style>
#growing {
*!*
transition: font-size 3s, color 2s;
*/!*
}
</style>
<script>
growing.onclick = function() {
this.style.fontSize='36px';
this.style.color='red';
}
</script>
Далее мы рассмотрим свойства анимации по отдельности.
transition-property
Список свойств, которые будут анимироваться, например: left
, margin-left
, height
, color
.
Анимировать можно не все свойства, но многие. Значение all
означает "анимировать все свойства".
transition-duration
Продолжительность анимации, задаётся в формате CSS time, то есть в секундах s
или ms
.
transition-delay
Задержка до анимации. Например, если transition-delay: 1s
, то анимация начнётся через 1 секунду после смены свойства.
Возможны отрицательные значения, при этом анимация начнётся с середины.
Например, вот анимация цифр от 0
до 9
:
[codetabs src="digits"]
Она осуществляется сменой margin-left
у элемента с цифрами, примерно так:
#stripe.animate {
margin-left: -174px;
transition-property: margin-left;
transition-duration: 9s;
}
В примере выше JavaScript просто добавляет элементу класс -- и анимация стартует:
digit.classList.add('animate');
Можно стартовать её "с середины", с нужной цифры, например соответствующей текущей секунде, при помощи отрицательного transition-delay
.
В примере ниже при клике на цифру она начнёт двигаться с текущей секунды:
[codetabs src="digits-negative-delay"]
В JavaScript это делается дополнительной строкой:
stripe.onclick = function() {
var sec = new Date().getSeconds() % 10;
*!*
// например, значение -3s начнёт анимацию с 3й секунды
stripe.style.transitionDelay = '-' + sec + 's';
*/!*
stripe.classList.add('animate');
};
transition-timing-function
Временнáя функция, которая задаёт, как процесс анимации будет распределён во времени, например начнётся ли анимация медленно, чтобы потом ускориться или наоборот.
Самое сложное, но при небольшом изучении -- вполне очевидное свойство.
У него есть два основных вида значения: кривая Безье и по шагам. Начнём с первого.
Кривая Безье
В качестве временной функции можно выбрать любую кривую Безье с 4 опорными точками, удовлетворяющую условиям:
- Начальная точка `(0,0)`.
- Конечная точка `(1,1)`.
- Для промежуточных точек значения `x` должны быть в интервале `0..1`, `y` -- любыми.
Синтаксис для задания кривой Безье в CSS: cubic-bezier(x2, y2, x3, y3)
. В нём указываются координаты только двух точек: второй и третьей, так как первая и последняя фиксированы.
Она указывает, как быстро развивается процесс анимации во времени.
- i>
- По оси `x` идёт время: `0` -- начальный момент, `1` -- конец времени `transition-duration`.
- По оси `y` -- завершённость процесса: `0` -- начальное значение анимируемого свойства, `1` -- конечное.
Самый простой вариант -- это когда процесс развивается равномерно, "линейно" по времени. Это можно задать кривой Безье
cubic-bezier(0, 0, 1, 1)
.График этой "кривой" таков:
...Как видно, это просто прямая. По мере того, как проходит время
x
, завершённость анимацииy
равномерно приближается от0
к1
.Поезд в примере ниже с постоянной скоростью "едет" слева направо, используя такую временную функцию:
[codetabs src="train-linear"]
CSS для анимации:
.train { left: 0; transition: left 5s cubic-bezier(0, 0, 1, 1); /* JavaScript ставит значение left: 450px */ }
Как нам показать, что поезд тормозит?
Для этого используем кривую Безье:
cubic-bezier(0.0, 0.5, 0.5 ,1.0)
.График этой кривой:
Как видно, процесс вначале развивается быстро -- кривая резко идёт вверх, а затем всё медленнее, медленнее.
Вы можете увидеть эту временную функцию в действии, кликнув на поезд:
[codetabs src="train"]
CSS для анимации:
.train { left: 0; transition: left 5s cubic-bezier(0, .5, .5, 1); /* JavaScript ставит значение left: 450px */ }
Существует несколько стандартных обозначений кривых:
linear
,ease
,ease-in
,ease-out
иease-in-out
.Значение
linear
-- это прямая, мы её уже видели.Остальные кривые являются короткой записью следующих
cubic-bezier
:`ease`* `ease-in` `ease-out` `ease-in-out` `(0.25, 0.1, 0.25, 1.0)` `(0.42, 0, 1.0, 1.0)` `(0, 0, 0.58, 1.0)` `(0.42, 0, 0.58, 1.0)` -
- По умолчанию, если никакой временной функции не указано, -- используется
ease
.
- По умолчанию, если никакой временной функции не указано, -- используется
Значени
Кривая Безье может заставить анимацию "выпрыгивать" за пределы диапазона.
Допустимо указывать для кривой Безье как отрицательные
y
, так и сколь угодно большие. При этом кривая Безье будет также поy
выскакивать за пределы диапазона0..1
, представляющего собой начало-конец значения.В примере ниже CSS-код анимации таков:
.train { left: 100px; transition: left 5s cubic-bezier(.5, -1, .5, 2); /* JavaScript поменяет left на 400px */ }
Свойство
left
должно меняться от100px
до400px
.Однако, если кликнуть на поезд, то мы увидим, что:
- Он едет сначала назад, то есть `left` становится меньше `100px`.
- Затем вперёд, причём выезжает за назначенные `400px`.
- А затем опять назад -- до `400px`.
[codetabs src="train-over"]
Почему так происходит -- отлично видно, если взглянуть на кривую Безье с указанными опорными точками:
Мы вынесли координату
y
для второй опорной точки на 1 ниже нуля, а для третьей опорной точки -- на 1 выше единицы, поэтому и кривая вышла за границы "обычного" квадрата. Её значения поy
вышли из стандартного диапазона0..1
.Как мы помним, значению
y = 0
соответствует "нулевое" положение анимации, аy = 1
-- конечное. Получается, что значенияy<0
двинули поезд назад, меньше исходногоleft
, а значенияy>1
-- больше итоговогоleft
.Это, конечно, "мягкий" вариант. Если поставить значения
y
порядка-99
,99
, то поезд будет куда более сильно выпрыгивать за диапазон.Итак, кривая Безье позволяет задавать "плавное"" течение анимации. Подобрать кривую Безье вручную можно на сайте .
Шаги steps
Временная функция
steps(количество шагов[, start/end])
позволяет разбить анимацию на чёткое количество шагов.Проще всего это увидеть на примере. Выше мы видели плавную анимацию цифр от
0
до9
при помощи сменыmargin-left
у элемента, содержащего0123456789
.Чтобы цифры сдвигались не плавно, а шли чётко и раздельно, одна за другой -- мы разобьём анимацию на 9 шагов:
#stripe.animate { margin-left: -174px; transition: margin-left 9s *!*steps(9, start)*/!*; }
В действии
step(9, start)
:[codetabs src="step"]
Первый аргумент
steps
-- количество шагов, то есть изменениеmargin-left
разделить на 9 частей, получается примерно по19px
. На то же количество частей делится и временной интервал, то есть по1s
.start
-- означает, что при начале анимации нужно сразу применить первое изменение. Это проявляется тем, что при нажатии на цифру она меняется на1
(первое изменениеmargin-left
) мгновенно, а затем в начале каждой следующей секунды.То есть, процесс развивается так:
- `0s` -- `-19px` (первое изменение в начале 1й секунды, сразу при нажатии)
- `1s` -- `-38px`
- ...
- `8s` -- `-174px`
- (на протяжении последней секунды видно окончательное значение).
Альтернативное значение
end
означало бы, что изменения нужно применять не в начале, а в конце каждой секунды, то есть так:- `0s` -- `0`
- `1s` -- `-19px` (первое изменение в конце 1й секунды)
- `2s` -- `-38px`
- ...
- `9s` -- `-174px`
В действии
step(9, end)
:[codetabs src="step-end"]
Также есть сокращённые значения:
- `step-start` -- то же, что `steps(1, start)`, то есть завершить анимацию в 1 шаг сразу.
- `step-end` -- то же, что `steps(1, end)`, то есть завершить анимацию в 1 шаг по истечении `transition-duration`.
Такие значения востребованы редко, так как это даже и не анимация почти, но тоже бывают полезны.
Событие transitionend
На конец CSS-анимации можно повесить обработчик на событие
transitionend
.Это широко используется, чтобы после анимации сделать какое-то действие или объединить несколько анимаций в одну.
Например, лодочка в примере ниже при клике начинает плавать туда-обратно, с каждым разом уплывая всё дальше вправо:
[iframe src="boat" height=300 edit link]
Её анимация осуществляется функцией
go
, которая перезапускается по окончании, с переворотом через CSS:boat.onclick = function() { //... var times = 1; function go() { if (times % 2) { // плывём вправо boat.classList.remove('back'); boat.style.marginLeft = 100 * times + 200 + 'px'; } else { // плывём влево boat.classList.add('back'); boat.style.marginLeft = 100 * times - 200 + 'px'; } } go(); boat.addEventListener('transitionend', function() { times++; go(); }); };
Объект события
transitionend
содержит специфические свойства:- `propertyName`
- Свойство, анимация которого завершилась.
- `elapsedTime`
- Время (в секундах), которое заняла анимация, без учета `transition-delay`.
Свойство
propertyName
может быть полезно при одновременной анимации нескольких свойств. Каждое свойство даст своё событие, и можно решить, что с ним делать дальше.CSS animations
Более сложные анимации делаются объединением простых при помощи CSS-правила
@keyframes
.В нём задаётся "имя" анимации и правила: что, откуда и куда анимировать. Затем при помощи свойства
animation: имя параметры
эта анимация подключается к элементу, задаётся время анимации и дополнительные параметры, как её применять.Например:
<!--+ run height=60 --> <div class="progress"></div> <style> /* Chrome, Opera, Safari */ @-webkit-keyframes MY-ANIMATION-NAME { from { left:0px; } to { left:300px; } } /* Other browsers, except IE9- */ @keyframes MY-ANIMATION-NAME { from { left:0px; } to { left:300px; } } .progress { position: relative; animation: MY-ANIMATION-NAME 3s infinite alternate; -webkit-animation: MY-ANIMATION-NAME 3s infinite alternate; border: 2px solid green; width: 50px; height: 20px; background: lime; } </style>
Этот стандарт пока в черновике, поэтому в Chrome, Safari, Opera нужен префикс
-webkit
.Статей про CSS animations достаточно много, посмотрите, например:
Итого
CSS-анимации позволяют плавно или не очень менять одно или несколько свойств.
Альтернатива им -- плавное изменение значений свойств через JavaScript, мы рассмотрим подробности далее.
Ограничения и достоинства CSS-анимаций по сравнению с JavaScript:
[compare] -Временная функция может быть задана кривой Безье или через шаги. Более сложные анимации, состоящие из нескольких кривых, реализуются их комбинацией при помощи CSS animations, но JavaScript-функции всегда гибче. -CSS-анимации касаются только свойств, а в JavaScript можно делать всё, что угодно, удалять элементы, создавать новые. -Отсутствует поддержка в IE9- +Простые вещи делаются просто. +"Легче" для процессора, чем анимации JavaScript, лучше используется графический ускоритель. Это очень важно для мобильных устройств. [/compare]
Подавляющее большинство анимаций делается через CSS.
При этом JavaScript запускает их начало -- как правило, добавлением класса, в котором задано новое свойство, и может отследить окончание через событие
transitionend
. -