Xiper

Анимация в CSS3. Часть II

Автор: Татьяна Шкабко, Александр Головко Дата публикации:

Материал является прямым продолжением статьи Анимация в CSS3. Часть I.

Можно ли заставить анимацию двигаться в обратную сторону?

Анимация может проигрываться либо только вперед, либо сначала вперед, а потом в обратном направлении (за это отвечает свойство animation-direction (в прямом виде оно пока не поддерживается браузерами, но Safari 4+ и Chrome 3+ поддерживают вендорное свойство -webkit-animation-direction). Проигрывание в обратном направлении возможно только тогда, когда анимация проигрывается больше одного цикла. Количество циклов устанавливает animation-iteration-count (-webkit-animation-iteration-count). В обратном направлении анимация может проигрываться только при четном цикле и значении -webkit-animation-direction:alternate. При проигрывании анимации в противоположном направлении противоположной становится также animation-timing-function (-webkit-animation-timing-function), например, если в прямом виде задано значение easy-in, то в обратном направлении будет easy-out, см. демо-пример (работает в Safari 4+ и Chrome 3+).

Можно ли присвоить одному элементу несколько анимаций?

Да. Это можно сделать, перечислив в свойстве animation-name (-webkit-animation-name) несколько имен анимаций через запятую и определив каждое имя с помощью правила @keyframes ( @-webkit-keyframes ). Остальные свойства анимации можно не дублировать, их значения будут автоматически применены ко всем анимациям элемента. Это может выглядеть, например, так:

div {
	position:relative;
	top: 50px;
	left: 0px;
	border-style: dashed;
	border-width: 0px;
	-webkit-animation-name: 'movement','coloring';
	-webkit-animation-duration: 10s;
	-webkit-animation-delay: 3s,5s;
	animation-name: 'movement','coloring';
	animation-duration: 10s;
	animation-delay: 3s,5s;
}
@-webkit-keyframes 'movement' {
    from {
	top: 50px;
	left: 0px;
    }
    50% {
	top: 150px;
	left: 100px;
    }
    to {
	top: 400px;
	left: 300px;
    }	
}
@-webkit-keyframes 'coloring' {
    from {
	background-color: #EEF;
	border: 1px dashed #539127;
    }
    to {
	background-color: #E8EDE3;
	border: 1px dashed #69F;
    }	
}
@keyframes 'movement' {
    from {
	top: 50px;
	left: 0px;
    }
    50% {
	top: 150px;
	left: 100px;
    }
    to {
	top: 400px;
	left: 300px;
    }	
}
@keyframes 'coloring' {
    from {
	background-color: #EEF;
	border: 1px dashed #539127;
    }
    to {
	background-color: #E8EDE3;
	border: 1px dashed #69F;
    }	
}

В данном случае к элементу применяется анимация перемещения movement и анимация раскраски coloring. Через 3с после загрузки страницы начинает применятся анимация передвижения и еще через 2с (итого через 5с после загрузки) применяется анимация раскраски. До применения анимации movement элемент имеет начальные координаты, а до применения анимации coloring — прозрачный фон и не имеет границ. Время обоих анимаций 10с, т.е. анимация перемещения закончится через 13с после загрузки страницы (3с задержка + 10с время), а анимация раскраски соответственно через 15с (5с задержки + 10с время).

Демо.

Можно ли несколько правил @keyframes объединить в одно?

Для уменьшения размера CSS файла возможно объединить несколько правил @keyframes в одно, но на практике это вызывает определенные затруднения. Рассмотрим пример:

div {
	position:relative;
	top: 50px;
	left: 0px;
	border:1px dashed transparent;
	-webkit-animation-name: 'movement','coloring';
	-webkit-animation-duration: 10s;
	-webkit-animation-timing-function: linear;
	animation-name: 'movement','coloring';
	animation-duration: 10s;
	animation-timing-function: linear;
}
@-webkit-keyframes 'movement' {
    from {
	top: 50px;
	left: 0px;
    }
    50% {
	top: 150px;
	left: 100px;
    }
    to {
	top: 400px;
	left: 300px;
    }	
}
@-webkit-keyframes 'coloring' {
    from {
	background-color: #EEF;
	border: 1px dashed #539127;
    }
    to {
	background-color: #E8EDE3;
	border: 1px dashed #69F;
    }	
}
@keyframes 'movement' {
    from {
	top: 50px;
	left: 0px;
    }
    50% {
	top: 150px;
	left: 100px;
    }
    to {
	top: 400px;
	left: 300px;
    }	
}
@keyframes 'coloring' {
    from {
	background-color: #EEF;
	border: 1px dashed #539127;
    }
    to {
	background-color: #E8EDE3;
	border: 1px dashed #69F;
    }	
}

Обе эти анимации проигрываются параллельно и ключевые кадры from и to описывают состояние анимации в один и тот же момент времени. В первой анимации еще есть ключевой кадр, описывающий анимацию движения через 50% времени анимации. На первый взгляд кажется, что их можно объединить так:

div {
	position:relative;
	top: 50px;
	left: 0px;
	border-style:dashed;
	border-width:0px;
	-webkit-animation-name: 'movementColoring';
	-webkit-animation-duration: 10s;
	animation-name: 'movementColoring';
	animation-duration: 10s;
}
@-webkit-keyframes 'movementColoring' {
    from {
	top: 50px;
	left: 0px;
	background-color: #EEF;
	border-color:#539127;
    }
    50% {
	top: 150px;
	left: 100px;
    }
    to {
	top: 400px;
	left: 300px;
	background-color: #E8EDE3;
	border-color: #69F;
    }	
}
@keyframes 'movementColoring' {
    from {
	top: 50px;
	left: 0px;
	background-color: #EEF;
	border-color:#539127;
    }
    50% {
	top: 150px;
	left: 100px;
    }
    to {
	top: 400px;
	left: 300px;
	background-color: #E8EDE3;
	border-color: #69F;
    }	
}

Демо.

Но что такое? Почему пропадает фоновый цвет и граница?

Вот это и есть первая трудность объединения 2х правил @-webkit-keyframes в одно: если значение какого-то из анимируемыех свойств не прописать в описании ключевого кадра, в этом ключевом кадре значение свойства будет не промежуточным между предыдущим и следующим ключевым кадром, как могло бы показаться на первый взгляд, а значением, которое было до применения анимации (в нашем случае на ключевом кадре 50% фоновый цвет и цвет границы станут прозрачными, как задано изначально).

Чтобы правильно объединить оба правила, нужно вручную рассчитать значения фонового цвета и цвета границы в момент времени 50% от времени анимации. Такой расчет теоретически возможен, но требует сложных математических подсчетов, учитывая, что значения изменяются по кривой Безье. Если взять частный случай кривой Безье — линейную кривую (-webkit-animation-timing-function: linear), то можно достаточно точно просчитать эти значения, применив линейную интерполяцию. В итоге получим:

div {
	position:relative;
	top: 50px;
	left: 0px;
	border-style:dashed;
	border-width:0px;
	-webkit-animation-name: 'movementColoring';
	-webkit-animation-duration: 10s;
	-webkit-animation-timing-function: linear;
	animation-name: 'movementColoring';
	animation-duration: 10s;
	animation-timing-function: linear;
}
@-webkit-keyframes 'movementColoring' {
    from {
	top: 50px;
	left: 0px;
	background-color: #EEF;
	border-color:#539127;
    }
    50% {
	top: 150px;
	left: 100px;
	background-color: #EBEDF1;
	border-color:#5C9593;
    }
    to {
	top: 400px;
	left: 300px;
	background-color: #E8EDE3;
	border-color: #69F;
    }	
}
@keyframes 'movementColoring' {
    from {
	top: 50px;
	left: 0px;
	background-color: #EEF;
	border-color:#539127;
    }
    50% {
	top: 150px;
	left: 100px;
	background-color: #EBEDF1;
	border-color:#5C9593;
    }
    to {
	top: 400px;
	left: 300px;
	background-color: #E8EDE3;
	border-color: #69F;
    }	
}

Сравнить объединенное правило @keyframes с раздельными, учитывая что -webkit-animation-timing-function: linear, можно на демо-страничке.

Помимо расчета значений могут возникнуть и другие сложности, например, задержка анимации. Как быть, например, с таким примером:

div {
	position:relative;
	top: 50px;
	left: 0px;
	border:1px dashed transparent;
	-webkit-animation-name: 'movement','coloring';
	-webkit-animation-duration: 10s;
	-webkit-animation-delay: 0s, 2s;
	-webkit-animation-timing-function: linear;
	animation-name: 'movement','coloring';
	animation-duration: 10s;
	animation-delay: 0s, 2s;
	animation-timing-function: linear;
}
@-webkit-keyframes 'movement' {
    from {
	top: 50px;
	left: 0px;
    }
    50% {
	top: 150px;
	left: 100px;
    }
    to {
	top: 400px;
	left: 300px;
    }	
}
@-webkit-keyframes 'coloring' {
    from {
	background-color: #EEF;
	border: 1px dashed #539127;
    }
    to {
	background-color: #E8EDE3;
	border: 1px dashed #69F;
    }	
}
@keyframes 'movement' {
    from {
	top: 50px;
	left: 0px;
    }
    50% {
	top: 150px;
	left: 100px;
    }
    to {
	top: 400px;
	left: 300px;
    }	
}
@keyframes 'coloring' {
    from {
	background-color: #EEF;
	border: 1px dashed #539127;
    }
    to {
	background-color: #E8EDE3;
	border: 1px dashed #69F;
    }	
}

Здесь у нас ключевые кадры приходятся на совершенно разные моменты времени, т.е. помимо промежуточных значений на каждый кадр, нам нужно также просчитать, на кокой момент времени объединенной анимации приходится тот или иной кадр. Такой вариант требует сложных математических подсчетов. Допустим, мы справились с этой задачей (не учитывая ошибки округления).

Для приведенной выше анимации приблизительно это будет выглядеть так:

div {
	position:relative;
	top: 50px;
	left: 0px;
	border:1px dashed transparent;
	-webkit-animation-name: 'movementColoring';
	-webkit-animation-duration: 12s;
	-webkit-animation-delay:none;
	-webkit-animation-timing-function: linear;
	animation-name: 'movementColoring';
	animation-duration: 12s;
	animation-delay:none;
	animation-timing-function: linear;
}
@-webkit-keyframes 'movementColoring' {
    from {
	top: 50px;
	left: 0px;
    }
    17% {
	top: 91px;
	left:41px;
	background-color: #EEF;
	border: 1px dashed #539127;
    }
    42% {
	top: 150px;
	left: 100px;
	background-color: #EAEDFB;
	border: 1px dashed #589368;
    }
    83% {
	top: 400px;
	left: 300px;
	background-color: #EDEEE9;
	border: 1px dashed #6297D2;
    }	
    to {
	background-color: #E8EDE3;
	border: 1px dashed #69F;
}
@keyframes 'movementColoring' {
    from {
	top: 50px;
	left: 0px;
    }
    17% {
	top: 91px;
	left:41px;
	background-color: #EEF;
	border: 1px dashed #539127;
    }
    42% {
	top: 150px;
	left: 100px;
	background-color: #EAEDFB;
	border: 1px dashed #589368;
    }
    83% {
	top: 400px;
	left: 300px;
	background-color: #EDEEE9;
	border: 1px dashed #6297D2;
    }	
    to {
	background-color: #E8EDE3;
	border: 1px dashed #69F;
}

Более наглядно оба варианта можно сравнить на демо-примере (смотреть Safari 4+, Chrome 3+).

Самая большая трудность в этом примере это то, что обе анимации не совпадают во времени. Т.е первых две секунды цвет менятся не должен а потом рывком появляется первый ключевой кадр для анимации цвета. Аналогично с движением: оно уже закончилось и элемент вернулся на начальную позицию, а цвет еще меняется. При совмещении анимаций цвет фона меняется не рывком через 2 секунды, как при задержке, а плавно от начального (прозрачного) до того, который описан в ключевом кадре 17%. Что касается движения, то в последние 2 секунды элемент смещается от конечной позиции анимации движения к начальным координатам, но опять же, не рывком, как при раздельных анимациях, а плавно.

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

Можно ли управлять CSS3 анимацией с помощью скриптов?

С помощью скриптов можно отключить анимацию полностью используя свойство animation (-webkit-animation), присвоив ему значение none. Таким образом отключаются все анимационные эффекты элемента. Анимацию можно не только отключать, но и ставить на паузу. Для этого используется свойство animation-play-state (-webkit-animation-play-state). Если присвоить этому свойству значение paused, анимация проигрываться не будет. Это можно применять в случаях, когда у элемента перечисленно несколько анимаций и нужно поставить на паузу только некоторые. Например, при трех анимациях, -webkit-animation-play-state: paused, running, paused, означает, что первая и третья анимация стоят на паузе, а вторая проигрывается. А еще наоборот, с помощью скриптов можно включать анимацию для элемента. Например, если для элемента с тремя анимациями -webkit-animation-play-state: paused, paused, paused, то можно скриптом переключить любое значение на running. Например, чтобы включить вторую анимацию элемента, нам нужно присвоить элементу значение -webkit-animation-play-state: paused, running, paused, третье и первое -webkit-animation-play-state: running, paused, running и т. д.

Демо-пример.

Еще один вариант применения -webkit-animation-play-state: paused — если необходимо, чтобы по окончанию анимации значения свойств не возвращались к первоначальным, ставим анимацию в самом конце на паузу.

Выводы

К сожалению, пользоваться CSS3-свойством animation пока достаточно трудно, имеется масса подводных камней, да еще в добавок преизрядные тормоза. На наш взгляд, js анимация все еще значительно мощнее и удобнее.

Материалы

  • W3C :: CSS Animations Module Level 3
  • :: Supported CSS Properties: Visual Effects
  • :: WebKit features not supported in AIR