CSS-анимации и переходы
Как упомянуто выше, многие нужды анимации могут быть реализованы посресдтсвом CSS, а не JavaScript-кода, исполняющего покадровую анимацию. Библиотека анимации WinJS, как мы уже видели, полностью построена на основе CSS. Использование CSS освобождает нас от необходимости писать большие объемы кода, который имеет дело с тем, как надо переместить каждый элемент в каждом кадре, основываясь на прошедшем времени и синхронизированно со скоростью обновления экрана. Вместо этого мы можем просто объявить то, что нам нужно (возможно, используя вспомогательные функции WinJS.UI.executeAnimation (http://msdn.microsoft.com/library/windows/apps/hh779762.aspx) и WinJS.UI.executeTransition ( http://msdn.microsoft.com/library/windows/apps/hh779763.aspx ) и предоставить хост-процессу приложения заботу о подробностях. Делегирование обязанностей в наилучшем виде! В этом разделе давайте внимательнее посмотрим на возможности CSS для приложений Магазина Windows.
Другое огромное преимущество выпонения анимаций и переходом посредством CSS – это то, что оно воздействует только на трансформацию и прозрачность элементов – это позволяет создавать то, что мы называем независимой анимацией, которая выполняется в потоке графического ядра вместо того, чтобы выполняться с использованием ресурсов центрального процессора. Зависимые анимации происходят, когда вы создаете их в JavaScript, используя временные интервалы, используем CSS-анимации и переходы со свойствами, отличными от трансформаций и изменения прозрачности, или выполняем анимацию на элементах, которые частично или полностью перекрыты другими элементами.
Мы вернемся к этой теме, когда посмотрим код примеров. Как я предполагаю, вы уже хотя бы немного знакомы с этим, поэтому сначала давайте посмотрим, как работают анимации и переходы в CSS. Я говорю об анимациях и переходах, так как это, на самом деле, две разные спецификации: CSS-анимации (http://www.w3.org/TR/css3-animations/) и CSS-переходы (http://www.w3.org/TR/css3-transitions/). Итак, в чем же разница?
Обычно, когда меняются CSS-свойства, их значения резко изменяются со старого значения на новое, что приводит к мгновенному визуальному изменения. Переходы сообщают хост-процессу приложения, как изменять одно или несколько значений свойств постепенно, в соответствии с заданной задержкой и временными параметрами. Все это объявляется в специальном правиле стиля для элемента (как для псевдо-элементов :before и :after) в применении к индивидуальным стилям:
transition-property (transitionProperty в JavaScript). Идентифицирует CSS-свойства, на которые воздействует переход (свойства, подвергаемые действию переходов (transitionable), которые перечислены в Разделе 7 спецификаций переходов).
transition-duration (transitionDuration в JavaScript). Задает длительность перехода в секундах (дробные значения секунд поддерживаются, например, .125s, отрицательные значения приводятся к 0s).
transition-delay (transitionDelay в JavaScript). Задает отложенное начало перехода, по отношению момента изменения свойства, в секундах. Если дано отрицательное значение, переход начнется раньше, но эффект не будет в это время виден.
transition-timing-function (transitionTimingFunction в JavaScript). Задает, как значение свойства будет меняться во времени, эти функции - ease, linear, ease-in, ease-out, ease-in-out, cubic-bezier, step-start, и step-end. В спецификациях W3C есть полезные диаграммы, которые поясняют это, но лучший способ увидеть разницу – попробовать все это на практике.
Например, переход для отдельного свойства выглядит так:
#div1 { transition-property: left; transition-duration: 2s; transition-delay: .5s; transition-timing-function: linear; }
При определении перехода для нескольких свойств, каждое значение в каждом стиле отделяется запятой:
.class2 { transition-property: opacity, left; transition-duration: 1s, 0.25s; }
Опять же, переходы не задают начальные и конечные значения свойств – они лишь определяют, как происходят изменения, независимо от того, установлены ли новые свойства посредством другого правила CSS или в JavaScript Так, в первом случае выше, если left изначально имеет значение 100px и установлено в 200px посредством правила :hover, оно будет меняться по прошествии 0.5 секунд со 100px до 300px в течение 2 секунд. Можно вычислить, что визуальное перемещение с временной функцией linear происходит со скоростью 100 пикселей в секунду. Другие временные функции покажут различную скорость перемещения в разные моменты в течение 2-х секунд.
Если JavaScript-код потом установит значение в -200 – в идеале – после завершения первого перехода и вызовет его событие thransitionend – значение снова изменится за то же время, но теперь – с 300px до -200px (всего 500px). В результате элемент будет перемещаться быстрее (250 пикселей в секунду, снова с помощью временной функции linear), так как ему нужно пройти большее пространство при той же продолжительности перехода.
Для переходов так же справедливо то, что если вы присваиваете элементу стиль (например, class2 выше), ничего не произойдет, до тех пор, пока свойство, на которое осуществляется воздействие, не изменит значение. Изменение стиля, наподобие этого, так же не возымеет действия, если исполняется анимация. Исключение – это если вы изменяете значение transition-property, в таком случае переход прекратится. В связи с этим, важно отметить, что значение этого свойства по умолчанию all, и его очистка (установка в "") не останавливает переход, а включает его! Вместо этого вам нужно установить данное свойство в none.
Примечание. Элементы с display: none вовсе не исполняют CSS-анимаций и переходов, по очевидным причинам. То же самое нельзя сказать об элементах с display: hidden, visibility: hidden, visibility: collapsed, или opacity: 0, что значит, что элемены с настройками, отличающимися от display: none могут исполнять анимацию невидимых элементов, что является пустой тратой ресурсов. Коротко говоря, используйте display: none.
Анимации работают не так, как переходы. Анимации определяются раздельно от любого правила стиля CSS, а затем присоединяются к правилам. Присвоение стиля элементу приводит к немедленному запуску анимации. Более того, группы зависимых свойств объявляются вместе, в ключевых кадрах,(keyframes), и, таким образом, анимируются вместе.
CSS-анимация, другими словами, это инструкция по постепенному обновлению одного или нескольких значений свойств CSS за некоторое время. Значения меняются от начального элемента к конечному, проходя через различные промежуточные состояния, заданные в наборе ключевых кадров. Вот пример (из Сценария 1 примера "Независимые анимации HTML" (http://code.msdn.microsoft.com/windowsapps/Independent-animations-app-c00b2962), на который мы будем ссылаться):
@keyframes move { from { transform: translateX(0px); } 50% { transform: translateX(400px); } to { transform: translateX(800px); } }
В более общем виде:
- Описание начинается с @keyframes <identifier>, где <identifier> - это любое имя, которое вы хотите назначить ключевому кадру (наподобие имени move выше). Вы будете ссылаться на этот идентификатор в правилах стиля.
- Внутри описания ключевого кадра, вы создаете любое количество наборов правил (rule sets), каждое из которых представляет разное состояние анимируемого элемента в различные этапы общего процесса анимации, отмеченные в процентах. Ключевые слова from и to, как показано выше, просто соответствуют, соответственно, 0% и 100%.
- В каждом наборе правил вы затем задаете желаемое значение любого количества свойств стиля (в примере выше это лишь transform), разделяя их точкой с запятой, как в CSS-стилях. Если значение для свойства не отличается от значения, установленного предыдущим правилом, анимация для данного свойства выполняться не будет. Если значения различаются, подсистема визуализации анимирует изменение между двумя значениями этого свойства в отрезок времени, эквивалентный <overall animation time> * (<toPercentage> <fromPercentage>)/100. Временная функция так же может быть задана для каждого набора правил с использованием стиля animation-timing-function, например:
50% { transform: translateX(400px); animation-timing-function: ease-in;}
Вы можете здесь заметить что хотя ключевой кадр может предусматривать использование временной функции, он ничего не сообщает о реальных таймингах. Это оставлено для установки в конкретном правиле стиля, которое ссылается на ключевой кадр. В Сценарии 1 примера, например:
.ball { animation-name: move; animation-duration: 2s; animation-timing-function: linear; animation-delay: 0s; animation-iteration-count: infinite; animation-play-state: running; }
- Здесь стиль animation-name (animationName в JavaScript) задает ключевой кадр для применения. Другие стили, , затем описывают то, как именно должен исполняться ключевой кадр:
- animation-duration (animationDuration в JavaScript). Длительность анимации в секундах (дробные значения секунд, конечно, разрешены). Отрицательные значения приравниваются 0s.
- animation-timing-function (animationTimingFunction в JavaScript). Задает, как и в случае с переходами, как значение свойства будет интерполироваться во времени: — ease (по умолчанию), linear, ease-in, ease-out, ease-in-out, cubic-bezier, step-start, и step-end.
- animation-delay (animationDelay в JavaScript). Определяет количество секунд, после которого анимация начнется после применения стиля. Значение может быть отрицательным, как в случае с переходами, что означает начало анимации на полпути ее обычного цикла.
- animation-iteration-count (animationIterationCount в JavaScript). Показывает, сколько раз повториться анимация (значение по умолчанию – 1). Это может быть либо числом, либо значением infinite, как показано выше.
- animation-direction (animationDirection в JavaScript). Показывает, следует ли анимации воспроизводиться в режиме normal (в прямом направлении), reverse, alternate (туда и обратно), или alternate-reverse (туда и обратно, начиная с обратного направления). Значение по умолчанию – normal.
- animation-play-state (animationPlayState в JavaScript). Позволяет проигрывать или приостанавливать анимацию. Состояние по умолчанию –running – проигрывает анимацию. Установка в paused приостанавливает анимацию до тех пор, пока вы не установите стиль обратно в значение running.
- animation-fill-mode (animationFillMode in JavaScript). Задает, какое значение свойства упомянутого ключевого кадра будет применено, когда анимация не исполняется, например, в течение первоначальной задержки или после завершения анимации. Значение по умолчанию none применяет значения для 0% или from из набора правил, если направление анимации установлено в forward и для направления alternate; применяются значения для 100% или to из набора правил, если направление установлено в reverse или alternate-reverse. Режим заполнения backwards выполняет обратные действия. Режим заполнения forwards всегда применяет значения для 100% или to (если только количество повторений не равно нулю, в таком случае это работает как backwards). Другие параметры, оба, это то же самое, что указание и forwards и backwards.
- animation (animation in JavaScript). Короткое имя стиля для всего вышеперечисленного (за исключением play-state) в порядке name (имя), duration (длительность), timing function (временная функция), delay (задержка), iteration count (число повторений), direction (направление), и fill mode (режим заполнения).
Применение стиля, который содержит animation-name запускает анимацию для элемента. Это может произойти автоматически, если анимация упомянута в стиле, который применен по умолчанию. Это так же может произойти по запросу, если стиль назначен элементу в JavaScript, или если вы зададите свойство элемента animation.
Ключевые кадры, которые обычно задаются в CSS, так же могут быть определены в JavaScript. Первый шаг заключается в построении строки, которая соответствует тому, что вы записали бы в CSS, затем эту строку вставляют в таблицу стилей. Это показано в Сценарии 7 примера независимых анимаций HTML (js/scenario7.js):
var styleSheet = document.styleSheets[1]; var element1 = document.getElementById("ballcontainer"); var animationString = '@keyframes bounce1 {' // ... + '}'; styleSheet.insertRule(animationString, 0); window.setImmediate(function () { element1.style.animationName = 'bounce1'; });
Обратите внимание на то, как этот код использует setImmediate для выхода из потока пользовательского интерфейса перед установкой свойства animationName, которое запускает анимацию. Это гарантирует то, что другой код (здесь не показанный), будет исполнен в первую очередь, так как он выполняет некоторые другие действия, которые должны быть завершены прежде чем начнется анимация.
В целом, полезно снова напомнить, что CSS-анимации и переходы начинаются только тогда, когда вы возвращаетесь из функции, которая их задает. Таким образом, ничего не отобразится до тех пор, пока вы не вернетесь обратно в поток пользовательского интерфейса и подсистема визуализации снова примется за свою работу, так же, как происходит, когда вы изменяете свойства, не имеющие отношения к анимации. Это означает, что вы можете настроить столько анимаций и переходов, сколько нужно, и они все будут исполнены одновременно. Использование обратного вызова с setImmediate, как показано выше, это просто способ сообщить системе: "Выполни этот код как только в потоке пользовательского интерфейса не будет другой работы"1Подробности об этом вы можете найти здесь: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/setImmediate/Overview.html . Подобный шаблон обычно применим для запуска одной или нескольких анимаций, когда все остальное настр оено.
В качестве последнего замечания для этого раздела, хочу отметить, что вам может быть интересен материал "Руководство по CSS-анимации: принципы и примеры" (http://coding.smashingmagazine.com/2011/09/14/the-guide-to-css-animation-principles-and-examples/) из Smashing Magazine. Из материала вы узнаете много всего об анимациях, помимо того, как задавать их в коде.