Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript

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, как показано выше, это просто способ сообщить системе: "Выполни этот код как только в потоке пользовательского интерфейса не будет другой работы"Подробности об этом вы можете найти здесь: 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. Из материала вы узнаете много всего об анимациях, помимо того, как задавать их в коде.

    Пример независимой анимации

    Обратимся к примеру "Независимые анимации HTML" (http://code.msdn.microsoft.com/windowsapps/Independent-animations-app-c00b2962), в Сценарии 1 показана демонстрация сравнения независимой и зависимой анимации при создании некоторой нагрузки на поток пользовательского интерфейса (то есть, блокировки этого потока) в соответствии с ползунком. В результате, верхний красный шар (смотрите изображение ниже) перемещается прерывисто, особенно, когда вы увеличиваете нагрузку на поток пользовательского интерфейса, перемещая ползунок. Зеленый шар внизу, с другой стороны, перемещается все время плавно.

    Непросто для понимания то, что в этом примере оба шара используют одно и то же правило стиля CSS, которое называется ball, которое мы видели в предыдущем разделе. На самом деле, практически во всем эти элементы совпадают. Почему же красный шар перемещается прерывисто, когда поток пользовательского интерфейса занят дополнительной работой?

    Секрет заключается в стиле z-index: -1; для красного шара в css/scenario1.css (и, соответственно, отсутствие position: static, что делает отрицательным z-index). Для независимо выполняемой анимации, элемент не должен перекрываться. Это, на самом деле, касается того, как создается макет в подсистеме визуализации HTML/CSS хост-процесса приложения, как элемент, находящийся где-то среди других, в соответствии с порядком взаимного расположения, может быть зависимым или независимым. Коротко говоря, стиль z-index является основным стилем, который может вам позволить сделать это.

    Как я упоминал выше, независимые анимации ограничены воздействием на свойства элемента ) в документации. Например, если в системе перегружен графический процессор, если вы нагружаете его слишком большим количеством независимых анимаций, или если элементы слишком велики, некоторые анимации превратятся в зависимые. Это – еще одна хорошая причина использовать анимации целенаправленно – чрезмерное их использование создает ужасный опыт взаимодействия на маломощных устройствах, таким образом, использование анимаций для повышения качества опыта взаимодействия пользователя и системы теряет смысл.

    Другой сценарий в примере позволяет вам поэкспериментировать с CSS-переходами и анимациями, задавая значения в различных элементах управления и затем запуская анимацию. Сценарии 2 и 3 работают с CSS-переходами для 2D и 3D-трансформаций, соответственно, эффект, создаваемый последним сценарием, показан на рис. 8.1. Как вы можете видеть, элемент, который анимирован в примере – это контейнер для всех элементов управления для ввода данных! Сценарии 5 и 6 позволяют вам делать похожие вещи с CSS-анимациями. Во всех этих случаях, необходимые стили задаются непосредственно в JavaScript, вместо использования декларативного CSS, поэтому для того, чтобы узнать подробности обратитесь к .js-файлам вместо .css-файлов. (рис 8.1) Вывод данных в Сценарии 3 примера независимых анимации HTML

    Сценарии 3 и 7, в свою очередь, показывают кое-что такое, о чем мы еще не говорили, несколько простых событий, которые вызываются для переходов и анимаций (независимые и зависимые анимации здесь не сравниваются). В последнем случае, любой элемент, для которого вы исполняете CSS-переход, вызывает события transitionstart и transitionended. Вы можете использовать эти события для объединения анимаций в цепочку.

    В случае с анимациями, имеются три события: animationstart (которое происходит после истечения срока задержки), animationend (при завершении анимации), и animationiteration (в конце каждого цикла анимации, если только тогда же не вызывается animationend). Как и в случае с переходами, эти события можно использовать для объединения анимаций в цепочку или синхронизации. Событие animationiteration так же полезно, если вам нужно выполнить некоторый код каждый раз, когда завершается очередной цикл анимации. В подобном обработчике вы можете проверить условия, которые могут привести к остановке анимации, в подобном случае вы можете установить animationPlayState в состояние приостановки, если нужно.

    Создание собственных анимаций: советы и секреты

    Если вы чем-то похожи на меня, я могу думаю, что первым, что вы сделали, когда начали экспериментировать с анимациями JavaScript, были: настройка начальных условий, создание таймеров с помощью ).

    Таким образом, сообщество разработчиков накопило немало ценных сведений по этим вопросам, если вы решили заняться этим. Я сказал так, потому что сейчас, посмотрев на библиотеку анимаций WinJS и на возможности CSS, вы вполне можете решить, стоит ли вам вообще этим заниматься. Некоторые люди подсчитали, что подавляющиее большинство анимаций, необходимых большинству приложений, могут быть полностью реализованы посредством CSS: просто задайте стиль и позвольте хост-процессу приложения сделать все остальное. Но если вы решили, что вам все еще нужна низкоуровневая анимация, вот первое, о чем вы должны спросить себя:"Каков подходящий интервал анимации?"

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

    Честно говоря, 10 мс – это излишество по целому ряду причин. 60 кадров в секунду (frames per second, fps) соответствует интервалу анимации в 16,7 мс – это меньше, чем способен различить человек и лучше, чем способно отобразить множество дисплеев. На самом деле, лучшие результаты получаются, когда кадры вашей анимации синхронизированы с частотой обновления дисплея.

    Рассмотрим это подробнее. Вы когда-нибудь смотрели на экран, когда ели что-нибудь хрустящее и замечали, что пиксели выглядят "пляшущими"? Это потому что дисплей – это не просто пассивное средство просмотра того, что находится в видеопамяти. Вместо этого дисплеи (даже LCD и LED-дисплеи) обычно просматривают видеопамять с заданной частотой обновления, которая обычно равняется 60 Гц, или 60 fps (но может равняться и 50 Гц, и 100 Гц).

    Это означает, что попытка вывода анимации с интервалом, меньшим, чем частота обновления дисплея – это пустая трата времени, пустая трата энергии (было показано, что это способно сократить время работы от батарей на 25%!), это приводит к выпадению кадров. Последнее заявление проиллюстрировано ниже, где красные точки – это кадры, которые были нарисованы на чем-то вроде элемента canvas, но никогда не появились на экране, так как другой кадр был нарисован до обновления экрана:

  • Обновление элемента canvas с интервалом менее 10 мс (Canvas update (<10ms intervals))
  • Интервалы обновления дисплея(Display Refresh Interfals)
  • Время (Time)
  • Именно поэтому обычно анимацию выводят с интервалами, кратными 16,7 мс, испольуя setInterval. Однако, использование значения в 16,7 подразумевает наличие дисплея, имеющего частоту обновления в 60 Гц, а это не всегда так. Правильное решение, таким образом, и для приложений Магазина Windows на JavaScript, и для веб-приложений – использовать requestAnimationFrame. Это API просто принимает функцию, которую нужно вызывать для каждого кадра:

    requestAnimationFrame(renderLoop);

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

    Более того, requestAnimationFrames принимает во внимание и видимость, что означает, что если элемент невидим (и анимация, таким образом, бесполезна), кадры отрисовываться не будут. Это означает, что вам не нужно самостоятельно обрабатывать события изменения видимости страницы для включения или выключения анимации: вы просто можете положиться на requestAnimationFrames.

    Совет. Если вы по-настоящему хотите оптимизировать вывод данных, постарайтесь выполнять все задачи рисования (а не только анимации) с помощью обратного вызова requestAnimationFrames. Таким образом, при обработке изменений, например, при реакции на событие ввода, обновляйте данные и вызывайте requestAnimationFrames с вашей функцией отрисовки вместо того, чтобы напрямую вызывать эту функцию. И всегда перерисовывайте что-либо только тогда, когда вам нужно сделать это, как мы скоро увидим, для того, чтобы наилучшим способом использовать процессор и батареи.

    Полезно знать, что попытка анимации элемента canvas, который частично перекрыт элементом с display: inline-block, приведет к значительному падению производительности и большим промежуткам между кадрами из-за чрезмерного аннулирования областей. Использование различных моделей отображения, таких, как table-cell, помогает избежать этой проблемы.

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

    Первый вызов requestAnimationFrames происходит в методе страницы ready, затем обратный вызов выполняет запрошенные изменения (js/scenario1.js):

    window.requestAnimationFrame(renderLoopRAF);
    
    function renderLoopRAF() {	
    drawClock();	
    window.requestAnimationFrame(renderLoopRAF);
    }	
    

    Здесь функция drawClock принимает текущее время и вычисляет угол для рисования стрелок часов:

    function drawClock() {
    // ...
    
    // Обратите внимание: пример модифицирован, Date создается лишь однажды, а не каждый раз
    var date = new Date();	
    var hour = date.getHours();	
    var minute = date.getMinutes();	
    var second = date.getSeconds();	
    
    // ...
    
    var sDegree = second / 60 * 360 - 180;
    var mDegree = minute / 60 * 360 - 180;
    var hDegree = ((hour + (minute / 60)) / 12) * 360 - 180;
    
    // Код для использования методов контекста translate, rotate, и drawImage 
    // для рисования каждой стрелки
    }
    

    Вот вам вопрос: "Что не так с этим кодом?". Запустите пример и посмотрите на секундную стрелку. Затем подумайте о том, как requestAnimationFrames обеспечивает соответствие с циклами обновления экрана с интервалом 16,7 мс. Что не так с изображением?

    Проблема здесь в том, что хотя секундная стрелка совершает видимые перемещения раз в секунду, код метода drawClock исполняется примерно 50, 60 или 100 раз в секунду – то есть гораздо чаще, чем нужно!. Таким образом, заголовок "Эффективные и плавные анимации" примера, означает все, что угодно, кроме того, что мы видим! На самом деле, если вы запустите Диспетчер задач, вы можете видеть, что эти простые и "эффективные" часы, по иронии судьбы, потребляют 15-20% ресурсов процессора. Ну ничего ж себе!

    Помните, что интервалы ориентированы на частоту обновления экрана в 16,7 мс (для 60 Гц-экрана), что подразумевает рендеринг 60 кадров в секунду. Если вам не нужно так много, вам следует пропускать кадры самостоятельно, ориентируясь на время, и экономить таким образом энергию, а не слепо перерисовывать экран, как сделано в этом примере. На самом деле, все, что нам нужно в подобных часах – это одно перемещение стрелки в секунду, постоянное вызывание requestAnimationFrames – это сущее излишество. Мы можем, вместо этого, использовать конструкцию setInterval(function () { requestAnimationFrame(drawClock) }, 1000) для того, чтобы скоординировать необходимый нам 1-секундный интервал с частотой обновления экрана. Если вы внесете это изменение, например, в метод ready, нагрузка на процессор значительно снизится:

    Но допустим, мы, на самом деле хотим выполнять анимацию с частотой 60 Гц и потребление при этом 20% процессорного времени нас устраивает. В подобном случае нам следует, как минимум, сделать движения секундной стрелки более плавными, что можно сделать, просто использовав миллисекунды при вычислении угла:

    var second = date.getSeconds() + date.getMilliseconds() / 1000;

    Тем не менее, 20% мощности процессора – это слишком много на что-то столь простое, и 60 кадров в секунду, это все еще серьезное излишество. Возможно, что-то около 10 кадров в секунду нам вполне подойдет. В таком случае мы можем вычислять прошедшее время в renderLoopRAF и вызывать drawClock только тогда, когда прошла 0,1 секунды:

    var lastTime = 0;
    
    function renderLoopRAF() {	
    var fps = 10; // Целевое значение кадров в секунду	
    var interval = 1000 / fps;	
    var curTime = Math.floor(Date.now() / interval);
    
    if (lastTime != curTime) {
    lastTime = curTime;	
    drawClock();	
    }	
    
    requestAnimationFrame(renderLoopRAF);
    }
    

    Движения уже не такие плавные, вывод с частотой 10 кадров в секунду создает впечатление механических движений, но такой подход позволяет значительно снизить нагрузку на процессор:

    Предлагаю вам поэкспериментировать со всем этим для того, чтобы увидеть, интервалы какой длины вы способны различить. 10 кадров в секунду и 15 дают ощущение механических движений, на 20 кадрах в секунду я не вижу особой разницы с 60-ю, а загрузка процессора состоавляет 7 – 10%. Вы можете так же попробовать что-то вроде 4 кадров в секунду (интервалы в четверть секунды) для того, чтобы увидеть их эффект. В дополнительных материалах к этой лекции я разместил вариант исходного примера, где вы можете выбирать из различных целевых частот рендеринга.

    Еще в этом примере можно реализовать рисование часовой и минутной стрелки с использованием дробных углов. В исходном коде, минутрая стрелка перемещается сразу же, когда секундная выполняет один оборот. Многие аналоговые часы работают не так: их сложные механизмы перемещают часовую и минутрую стрелки понемного с каждой секундой. Для имитации такого поведения нам всего лишь нужно использовать вычисление секунд при вычислении минут, и использовать минуты при вычислении часов, например, так:

    var second = date.getSeconds() + date.getMilliseconds() / 1000;
    var minute = date.getMinutes() + second / 60;
    var hour  = date.getHours() + minute / 60;
    

    При реальном использовании, например, в игре, обычно избегают выполнения бесконечных циклов анимации, как здесь: если по экрану не перемещается ничего, что должно анимироваться (для чего вы можете использовать ), чтобы узнать, отображается ли ваше приложение на экране. Так как requestAnimationFrames принимает во внимание видимость (использование примером процессора падает до 0% перед приостановкой), вам нужно это сделать с помощью setTimeout и setInterval.

    В итоге, главная цель здесь – по-настоящему понять, какой интервал анимации вам нужен (то есть, какая частота кадров) для того, чтобы наилучшим образом использовать requestAnimationFrames, если это нужно, или использовать setInterval/setTimeOut. И у того и у другого есть собственные варианты использования, которые позволяют предоставить хороший опыт взаимодействия пользователя и приложения при соответствующей нагрузке на систему.

    Знаете ли вы? Одно из изменений в Windows 8 и Internen Explorer заключается в том, что setTimeout, setInterval, вместе с setImmediate, поддерживают включение параметров, которые вы можете передать в функцию обратного вызова?

    Что мы только что изучили

  • В панели управления на Рабочем столе, пользователи могут задать параметр, отключающий большинство анимаций, не являющихся существенными для работы системы. Приложениям следует принимать это во внимание, как это делает WinJS, проверяя свойство Windows.UI.ViewManagement.UISettings.animationsEnabled.
  • Библиотека анимаций WinJS содержит множество встроенных анимаций, которые воплощают индивидуальность Windows 8. Они рекомендованы для использования в соответствующих им сценариях приложения, в таких, как переходы содержимого и страниц, выделение, манипуляции со списками и в других.
  • Все анимации WinJS построены на основе CSS и, таким образом, используют преимущества аппаратного ускорения. Когда соблюдены верные условия, такие анимации исполняются на GPU, и, таким образом, не подвержены влиянию деятельности в потоке пользовательского интерфейса.
  • Приложения так же могут непосредственно использовать CSS-анимации и переходы в соответствии со спецификациями W3C.
  • Помимо возможностей WinJS и CSS, приложения могут использовать функции наподобие setInterval и requestAnimationFrame для реализации непосредственной покадровой анимации. Метод requestAnimationFrame синхронизирует отрисовку кадров с частотой обновления экрана, что ведет к лучшей общей производительности.
  • Страницы:

    Как упомянуто выше, многие нужды анимации могут быть реализованы посресдтсвом 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, как показано выше, это просто способ сообщить системе: "Выполни этот код как только в потоке пользовательского интерфейса не будет другой работы"Подробности об этом вы можете найти здесь: 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. Из материала вы узнаете много всего об анимациях, помимо того, как задавать их в коде.

    Пример независимой анимации

    Обратимся к примеру "Независимые анимации HTML" (http://code.msdn.microsoft.com/windowsapps/Independent-animations-app-c00b2962), в Сценарии 1 показана демонстрация сравнения независимой и зависимой анимации при создании некоторой нагрузки на поток пользовательского интерфейса (то есть, блокировки этого потока) в соответствии с ползунком. В результате, верхний красный шар (смотрите изображение ниже) перемещается прерывисто, особенно, когда вы увеличиваете нагрузку на поток пользовательского интерфейса, перемещая ползунок. Зеленый шар внизу, с другой стороны, перемещается все время плавно.

    Непросто для понимания то, что в этом примере оба шара используют одно и то же правило стиля CSS, которое называется ball, которое мы видели в предыдущем разделе. На самом деле, практически во всем эти элементы совпадают. Почему же красный шар перемещается прерывисто, когда поток пользовательского интерфейса занят дополнительной работой?

    Секрет заключается в стиле z-index: -1; для красного шара в css/scenario1.css (и, соответственно, отсутствие position: static, что делает отрицательным z-index). Для независимо выполняемой анимации, элемент не должен перекрываться. Это, на самом деле, касается того, как создается макет в подсистеме визуализации HTML/CSS хост-процесса приложения, как элемент, находящийся где-то среди других, в соответствии с порядком взаимного расположения, может быть зависимым или независимым. Коротко говоря, стиль z-index является основным стилем, который может вам позволить сделать это.

    Как я упоминал выше, независимые анимации ограничены воздействием на свойства элемента ) в документации. Например, если в системе перегружен графический процессор, если вы нагружаете его слишком большим количеством независимых анимаций, или если элементы слишком велики, некоторые анимации превратятся в зависимые. Это – еще одна хорошая причина использовать анимации целенаправленно – чрезмерное их использование создает ужасный опыт взаимодействия на маломощных устройствах, таким образом, использование анимаций для повышения качества опыта взаимодействия пользователя и системы теряет смысл.

    Другой сценарий в примере позволяет вам поэкспериментировать с CSS-переходами и анимациями, задавая значения в различных элементах управления и затем запуская анимацию. Сценарии 2 и 3 работают с CSS-переходами для 2D и 3D-трансформаций, соответственно, эффект, создаваемый последним сценарием, показан на рис. 8.1. Как вы можете видеть, элемент, который анимирован в примере – это контейнер для всех элементов управления для ввода данных! Сценарии 5 и 6 позволяют вам делать похожие вещи с CSS-анимациями. Во всех этих случаях, необходимые стили задаются непосредственно в JavaScript, вместо использования декларативного CSS, поэтому для того, чтобы узнать подробности обратитесь к .js-файлам вместо .css-файлов. (рис 8.1) Вывод данных в Сценарии 3 примера независимых анимации HTML

    Сценарии 3 и 7, в свою очередь, показывают кое-что такое, о чем мы еще не говорили, несколько простых событий, которые вызываются для переходов и анимаций (независимые и зависимые анимации здесь не сравниваются). В последнем случае, любой элемент, для которого вы исполняете CSS-переход, вызывает события transitionstart и transitionended. Вы можете использовать эти события для объединения анимаций в цепочку.

    В случае с анимациями, имеются три события: animationstart (которое происходит после истечения срока задержки), animationend (при завершении анимации), и animationiteration (в конце каждого цикла анимации, если только тогда же не вызывается animationend). Как и в случае с переходами, эти события можно использовать для объединения анимаций в цепочку или синхронизации. Событие animationiteration так же полезно, если вам нужно выполнить некоторый код каждый раз, когда завершается очередной цикл анимации. В подобном обработчике вы можете проверить условия, которые могут привести к остановке анимации, в подобном случае вы можете установить animationPlayState в состояние приостановки, если нужно.

    Создание собственных анимаций: советы и секреты

    Если вы чем-то похожи на меня, я могу думаю, что первым, что вы сделали, когда начали экспериментировать с анимациями JavaScript, были: настройка начальных условий, создание таймеров с помощью ).

    Таким образом, сообщество разработчиков накопило немало ценных сведений по этим вопросам, если вы решили заняться этим. Я сказал так, потому что сейчас, посмотрев на библиотеку анимаций WinJS и на возможности CSS, вы вполне можете решить, стоит ли вам вообще этим заниматься. Некоторые люди подсчитали, что подавляющиее большинство анимаций, необходимых большинству приложений, могут быть полностью реализованы посредством CSS: просто задайте стиль и позвольте хост-процессу приложения сделать все остальное. Но если вы решили, что вам все еще нужна низкоуровневая анимация, вот первое, о чем вы должны спросить себя:"Каков подходящий интервал анимации?"

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

    Честно говоря, 10 мс – это излишество по целому ряду причин. 60 кадров в секунду (frames per second, fps) соответствует интервалу анимации в 16,7 мс – это меньше, чем способен различить человек и лучше, чем способно отобразить множество дисплеев. На самом деле, лучшие результаты получаются, когда кадры вашей анимации синхронизированы с частотой обновления дисплея.

    Рассмотрим это подробнее. Вы когда-нибудь смотрели на экран, когда ели что-нибудь хрустящее и замечали, что пиксели выглядят "пляшущими"? Это потому что дисплей – это не просто пассивное средство просмотра того, что находится в видеопамяти. Вместо этого дисплеи (даже LCD и LED-дисплеи) обычно просматривают видеопамять с заданной частотой обновления, которая обычно равняется 60 Гц, или 60 fps (но может равняться и 50 Гц, и 100 Гц).

    Это означает, что попытка вывода анимации с интервалом, меньшим, чем частота обновления дисплея – это пустая трата времени, пустая трата энергии (было показано, что это способно сократить время работы от батарей на 25%!), это приводит к выпадению кадров. Последнее заявление проиллюстрировано ниже, где красные точки – это кадры, которые были нарисованы на чем-то вроде элемента canvas, но никогда не появились на экране, так как другой кадр был нарисован до обновления экрана:

  • Обновление элемента canvas с интервалом менее 10 мс (Canvas update (<10ms intervals))
  • Интервалы обновления дисплея(Display Refresh Interfals)
  • Время (Time)
  • Именно поэтому обычно анимацию выводят с интервалами, кратными 16,7 мс, испольуя setInterval. Однако, использование значения в 16,7 подразумевает наличие дисплея, имеющего частоту обновления в 60 Гц, а это не всегда так. Правильное решение, таким образом, и для приложений Магазина Windows на JavaScript, и для веб-приложений – использовать requestAnimationFrame. Это API просто принимает функцию, которую нужно вызывать для каждого кадра:

    requestAnimationFrame(renderLoop);

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

    Более того, requestAnimationFrames принимает во внимание и видимость, что означает, что если элемент невидим (и анимация, таким образом, бесполезна), кадры отрисовываться не будут. Это означает, что вам не нужно самостоятельно обрабатывать события изменения видимости страницы для включения или выключения анимации: вы просто можете положиться на requestAnimationFrames.

    Совет. Если вы по-настоящему хотите оптимизировать вывод данных, постарайтесь выполнять все задачи рисования (а не только анимации) с помощью обратного вызова requestAnimationFrames. Таким образом, при обработке изменений, например, при реакции на событие ввода, обновляйте данные и вызывайте requestAnimationFrames с вашей функцией отрисовки вместо того, чтобы напрямую вызывать эту функцию. И всегда перерисовывайте что-либо только тогда, когда вам нужно сделать это, как мы скоро увидим, для того, чтобы наилучшим способом использовать процессор и батареи.

    Полезно знать, что попытка анимации элемента canvas, который частично перекрыт элементом с display: inline-block, приведет к значительному падению производительности и большим промежуткам между кадрами из-за чрезмерного аннулирования областей. Использование различных моделей отображения, таких, как table-cell, помогает избежать этой проблемы.

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

    Первый вызов requestAnimationFrames происходит в методе страницы ready, затем обратный вызов выполняет запрошенные изменения (js/scenario1.js):

    window.requestAnimationFrame(renderLoopRAF);
    
    function renderLoopRAF() {	
    drawClock();	
    window.requestAnimationFrame(renderLoopRAF);
    }	
    

    Здесь функция drawClock принимает текущее время и вычисляет угол для рисования стрелок часов:

    function drawClock() {
    // ...
    
    // Обратите внимание: пример модифицирован, Date создается лишь однажды, а не каждый раз
    var date = new Date();	
    var hour = date.getHours();	
    var minute = date.getMinutes();	
    var second = date.getSeconds();	
    
    // ...
    
    var sDegree = second / 60 * 360 - 180;
    var mDegree = minute / 60 * 360 - 180;
    var hDegree = ((hour + (minute / 60)) / 12) * 360 - 180;
    
    // Код для использования методов контекста translate, rotate, и drawImage 
    // для рисования каждой стрелки
    }
    

    Вот вам вопрос: "Что не так с этим кодом?". Запустите пример и посмотрите на секундную стрелку. Затем подумайте о том, как requestAnimationFrames обеспечивает соответствие с циклами обновления экрана с интервалом 16,7 мс. Что не так с изображением?

    Проблема здесь в том, что хотя секундная стрелка совершает видимые перемещения раз в секунду, код метода drawClock исполняется примерно 50, 60 или 100 раз в секунду – то есть гораздо чаще, чем нужно!. Таким образом, заголовок "Эффективные и плавные анимации" примера, означает все, что угодно, кроме того, что мы видим! На самом деле, если вы запустите Диспетчер задач, вы можете видеть, что эти простые и "эффективные" часы, по иронии судьбы, потребляют 15-20% ресурсов процессора. Ну ничего ж себе!

    Помните, что интервалы ориентированы на частоту обновления экрана в 16,7 мс (для 60 Гц-экрана), что подразумевает рендеринг 60 кадров в секунду. Если вам не нужно так много, вам следует пропускать кадры самостоятельно, ориентируясь на время, и экономить таким образом энергию, а не слепо перерисовывать экран, как сделано в этом примере. На самом деле, все, что нам нужно в подобных часах – это одно перемещение стрелки в секунду, постоянное вызывание requestAnimationFrames – это сущее излишество. Мы можем, вместо этого, использовать конструкцию setInterval(function () { requestAnimationFrame(drawClock) }, 1000) для того, чтобы скоординировать необходимый нам 1-секундный интервал с частотой обновления экрана. Если вы внесете это изменение, например, в метод ready, нагрузка на процессор значительно снизится:

    Но допустим, мы, на самом деле хотим выполнять анимацию с частотой 60 Гц и потребление при этом 20% процессорного времени нас устраивает. В подобном случае нам следует, как минимум, сделать движения секундной стрелки более плавными, что можно сделать, просто использовав миллисекунды при вычислении угла:

    var second = date.getSeconds() + date.getMilliseconds() / 1000;

    Тем не менее, 20% мощности процессора – это слишком много на что-то столь простое, и 60 кадров в секунду, это все еще серьезное излишество. Возможно, что-то около 10 кадров в секунду нам вполне подойдет. В таком случае мы можем вычислять прошедшее время в renderLoopRAF и вызывать drawClock только тогда, когда прошла 0,1 секунды:

    var lastTime = 0;
    
    function renderLoopRAF() {	
    var fps = 10; // Целевое значение кадров в секунду	
    var interval = 1000 / fps;	
    var curTime = Math.floor(Date.now() / interval);
    
    if (lastTime != curTime) {
    lastTime = curTime;	
    drawClock();	
    }	
    
    requestAnimationFrame(renderLoopRAF);
    }
    

    Движения уже не такие плавные, вывод с частотой 10 кадров в секунду создает впечатление механических движений, но такой подход позволяет значительно снизить нагрузку на процессор:

    Предлагаю вам поэкспериментировать со всем этим для того, чтобы увидеть, интервалы какой длины вы способны различить. 10 кадров в секунду и 15 дают ощущение механических движений, на 20 кадрах в секунду я не вижу особой разницы с 60-ю, а загрузка процессора состоавляет 7 – 10%. Вы можете так же попробовать что-то вроде 4 кадров в секунду (интервалы в четверть секунды) для того, чтобы увидеть их эффект. В дополнительных материалах к этой лекции я разместил вариант исходного примера, где вы можете выбирать из различных целевых частот рендеринга.

    Еще в этом примере можно реализовать рисование часовой и минутной стрелки с использованием дробных углов. В исходном коде, минутрая стрелка перемещается сразу же, когда секундная выполняет один оборот. Многие аналоговые часы работают не так: их сложные механизмы перемещают часовую и минутрую стрелки понемного с каждой секундой. Для имитации такого поведения нам всего лишь нужно использовать вычисление секунд при вычислении минут, и использовать минуты при вычислении часов, например, так:

    var second = date.getSeconds() + date.getMilliseconds() / 1000;
    var minute = date.getMinutes() + second / 60;
    var hour  = date.getHours() + minute / 60;
    

    При реальном использовании, например, в игре, обычно избегают выполнения бесконечных циклов анимации, как здесь: если по экрану не перемещается ничего, что должно анимироваться (для чего вы можете использовать ), чтобы узнать, отображается ли ваше приложение на экране. Так как requestAnimationFrames принимает во внимание видимость (использование примером процессора падает до 0% перед приостановкой), вам нужно это сделать с помощью setTimeout и setInterval.

    В итоге, главная цель здесь – по-настоящему понять, какой интервал анимации вам нужен (то есть, какая частота кадров) для того, чтобы наилучшим образом использовать requestAnimationFrames, если это нужно, или использовать setInterval/setTimeOut. И у того и у другого есть собственные варианты использования, которые позволяют предоставить хороший опыт взаимодействия пользователя и приложения при соответствующей нагрузке на систему.

    Знаете ли вы? Одно из изменений в Windows 8 и Internen Explorer заключается в том, что setTimeout, setInterval, вместе с setImmediate, поддерживают включение параметров, которые вы можете передать в функцию обратного вызова?

    Что мы только что изучили

  • В панели управления на Рабочем столе, пользователи могут задать параметр, отключающий большинство анимаций, не являющихся существенными для работы системы. Приложениям следует принимать это во внимание, как это делает WinJS, проверяя свойство Windows.UI.ViewManagement.UISettings.animationsEnabled.
  • Библиотека анимаций WinJS содержит множество встроенных анимаций, которые воплощают индивидуальность Windows 8. Они рекомендованы для использования в соответствующих им сценариях приложения, в таких, как переходы содержимого и страниц, выделение, манипуляции со списками и в других.
  • Все анимации WinJS построены на основе CSS и, таким образом, используют преимущества аппаратного ускорения. Когда соблюдены верные условия, такие анимации исполняются на GPU, и, таким образом, не подвержены влиянию деятельности в потоке пользовательского интерфейса.
  • Приложения так же могут непосредственно использовать CSS-анимации и переходы в соответствии со спецификациями W3C.
  • Помимо возможностей WinJS и CSS, приложения могут использовать функции наподобие setInterval и requestAnimationFrame для реализации непосредственной покадровой анимации. Метод requestAnimationFrame синхронизирует отрисовку кадров с частотой обновления экрана, что ведет к лучшей общей производительности.
  • Вернуться к учебному плану