CSS3-анимации и новый javascript-метод Animate(). Создание анимации в JavaScript Обратно: выстрел из лука

Здравствуйте! В этом уроке рассмотрим анимации в JavaScript. JavaScript-анимация применяется где не подходит .

Например, анимация по сложной траектории, с специальной временной функцией, выходящей за рамки кривых Безье, на canvas. Еще её используют для анимации в старых IE. Хотя надо сказать, что для экономии ресурсов, особенно мобильных устройств предпочтительно использовать CSS анимации.

Функция setInterval

Сначала давайте дадим определение анимации. Итак с точки зрения HTML/CSS, анимация – это постепенное изменение стиля DOM-элемента. Например, увеличение координаты style.left от 0px до 100px сдвигает элемент влево.

Если увеличивать свойство left от 0 до 100 при помощи метода , делая по 50 изменений в секунду, то это будет выглядеть как плавное перемещение. Тот же принцип, что и в киноиндустрии: для анимации достаточно 24 или больше вызовов setInterval в секунду.

Код для анимации выглядит так:

Var fps = 50; // 50 кадров в секунду var timer = setInterval(function() { if (время вышло) clearInterval(timer); else немного увеличить left } , 1000 / fps)

Вот более полный пример кода анимации:

Var start = Date.now(); // сохранить время начала var timer = setInterval(function() { // вычислить сколько времени прошло с начала анимации var timePass = Date.now() - start; if (timePassed >= 2000) { clearInterval(timer); // конец через 2 секунды return; } // рисует состояние анимации, соответствующее времени timePass draw(timePassed); }, 20); // в то время как timePassed идёт от 0 до 2000 // left принимает значения от 0 до 400px function draw(timePass) { train.style.left = timePass / 5 + "px"; }

requestAnimationFrame

Если у вас не один такой setInterval, а несколько в разных местах кода, то браузеру нужно в те же 20 мс работать со страницей уже не один раз. А ведь кроме setInterval есть ещё другие действия, к примеру, прокрутка страницы, которую тоже надо нарисовать.

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

Гораздо лучше с точки зрения производительности – сгруппировать все перерисовки в одну и запускать их централизованно, все вместе.

Для этого в JavaScript-фреймворках, которые поддерживают анимацию, есть единый таймер:

SetInterval(function() { /* отрисовать все анимации */ }, 20);

Все анимации, которые запускает такой фреймворк, добавляются в глобальный общий список, и раз в 20 мс единый таймер проверяет его, запускает текущие, удаляет завершившиеся
Современные браузеры, кроме IE9-, поддерживают стандарт Animation timing, который представляет собой дальнейший шаг в этом направлении. Он позволяет синхронизировать все анимации со встроенными механизмами обновления страницы. То есть, сгруппированы будут не только наши, но и CSS-анимации и другие браузерные перерисовки.

При этом графический ускоритель будет использован максимально эффективно, и исключена повторная обработка одних и тех же участков страницы. А значит – меньше будет загрузка CPU, да и сама анимация станет более плавной.

Для этого используется функция requestAnimationFrame.

Синтаксис функции:

Var requestId = requestAnimationFrame(callback)

Такой вызов планирует запуск функции callback в ближайшее время, когда браузер сочтёт возможным осуществить анимацию.
Если запланировать в callback какое-то рисование, то оно будет сгруппировано с другими requestAnimationFrame и с внутренними перерисовками браузера.
Возвращаемое значение в requestId нужно для отмены запуска:

// отменить запланированное выше выполнение callback cancelAnimationFrame(requestId);

Функция callback получает один аргумент – время, прошедшее с начала загрузки страницы, результат вызова performance.now().

Как правило, запуск callback происходит очень скоро. Если у процессора большая загрузка или батарея у ноутбука почти разряжена – то пореже.

Если вы запустите этот код, то увидите промежутки между первыми 20 запусками requestAnimationFrame. Как правило, это 10-20 мс, но бывает и больше и меньше. Это оптимальная частота анимации с точки зрения браузера.

var prev = performance.now(); var times = 0; requestAnimationFrame(function measure(time) { document.body.insertAdjacentHTML("beforeEnd", Math.floor(time - prev) + " "); prev = time; if (times++ < 10) requestAnimationFrame(measure); })

Функция анимации на основе requestAnimationFrame:

// Рисует функция draw // Продолжительность анимации duration function animate(draw, duration) { var start = performance.now(); requestAnimationFrame(function animate(time) { // определить, сколько прошло времени с начала анимации var timePassed = time - start; // возможно небольшое превышение времени, в этом случае зафиксировать конец if (timePassed > duration) timePassed = duration; // нарисовать состояние анимации в момент timePassed draw(timePassed); // если время анимации не закончилось - запланировать ещё кадр if (timePassed < duration) { requestAnimationFrame(animate); } }); }

Структура анимации

На основе requestAnimationFrame можно соорудить и гораздо более мощную, но в то же время простую функцию анимации.

У анимации есть три основных параметра:

Duration Общее время, которое длиться анимация, в мс. Например, 1000. timing(timeFraction) Временная функция, которая, по аналогии с , будет по текущему времени вычислять состояние анимации.

Она получает на вход непрерывно возрастающее число timeFraction – от 0 до 1, где 0 означает самое начало анимации, а 1 – её конец.

Её результатом должно быть значение завершённости анимации, которому в CSS transitions на кривых Безье соответствует координата y.

Также по аналогии с transition-timing-function должны соблюдаться условия:

  • timing(0) = 0
  • timing(1) = 1…То есть, анимация начинается в точке (0,0) – нулевое время и нулевой прогресс и заканчивается в (1, 1) – прошло полное время, и процесс завершён.Например, функция-прямая означает равномерное развитие процесса: function linear(timeFraction) { return timeFraction; }
  • Её график:

  • Как видно, её график полностью совпадает с transition-timing-function: linear, и эффект абсолютно такой же.Есть и другие, более интересные варианты, мы рассмотрим их далее.
draw(progress) Функция, которая получает состояние завершённости анимации и рисует его. Значению progress=0 соответствует начальная точка анимации, progress=1 – конечная.

Именно эта функция и осуществляет, собственно, анимацию.

Например, может двигать элемент:

Function draw(progress) { train.style.left = progress + "px"; } Возможны в принципе любые варианты, анимировать можно что угодно и как угодно.

Временные функции

Выше мы видели самую простую, линейную временную функцию.

Рассмотрим примеры анимации движения с использованием различных timing.

В степени n

Вот еще один простой случай – progress в степени n. Частные случаи – квадратичная, кубическая функции и т.д.

Для квадратичной функции:

Function quad(progress) { return Math.pow(progress, 2) }

График квадратичной функции:

Увеличение степени влияет на ускорение. Например, график для 5-й степени:

Функция:

Function circ(timeFraction) { return 1 - Math.sin(Math.acos(timeFraction)) }

График:

Back: стреляем из лука

Эта функция работает по принципу лука: сначала мы «натягиваем тетиву», а затем «стреляем».

В отличие от предыдущих функций, эта зависит от дополнительного параметра x, который является «коэффициентом упругости». Он определяет расстояние, на которое «оттягивается тетива».

Function back(x, timeFraction) { return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x) }

График для x = 1.5:

Отскок bounce

Представьте, что вы отпускаете мяч, он падает на пол, несколько раз отскакивает и останавливается.

Функция bounce делает то же самое, только наоборот: «подпрыгивание» начинается сразу.

Эта функция немного сложнее предыдущих и использует коэффициенты:

Function bounce(timeFraction) { for (var a = 0, b = 1, result; 1; a += b, b /= 2) { if (timeFraction >= (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) } } }

Упругая анимация

Эта функция зависит от дополнительного параметра x, который определяет начальный диапазон.

Function elastic(x, timeFraction) { return Math.pow(2, 10 * (timeFraction - 1)) * Math.cos(20 * Math.PI * x / 3 * timeFraction) }

График для x=1.5:

Реверсивные функции ease*

Итак, у нас есть коллекция временных функций.

Их использование называется «easeIn».

Иногда нужно показать анимацию в обратном режиме. Преобразование функции, которое даёт такой эффект, называется «easeOut».

easeOut

В режиме «easeOut», значение timing вычисляется по формуле: timingEaseOut(timeFraction) = 1 — timing(1 — timeFraction)

Например, функция bounce в режиме «easeOut»:

// обычный вариант function bounce(timeFraction) { for (var a = 0, b = 1, result; 1; a += b, b /= 2) { if (timeFraction >= (7 - 4 * a) / 11) { return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2); } } } // преобразователь в easeOut function makeEaseOut(timing) { return function(timeFraction) { return 1 - timing(1 - timeFraction); } } var bounceEaseOut = makeEaseOut(bounce);

Полный пример – отскок в bounceEaseOut теперь не в начале, а в конце (и это куда красивее):
На этом графике видно преобразование easeOut изменяет поведение функции:

Если есть анимационный эффект, такой как подпрыгивание – он будет показан в конце, а не в начале (или наоборот, в начале, а не в конце).

Красным цветом обозначен обычный вариант, а синим – easeOut.

  • Обычно анимируемый объект сначала медленно скачет внизу, а затем, в конце, резко достигает верха…
  • А после easeOut – он сначала прыгает наверх, а затем медленно скачет внизу.
easeInOut

А еще можно сделать так, чтобы показать эффект и в начале и в конце анимации. Соответствующее преобразование называется «easeInOut».

Его код выглядит так:

If (timeFraction 1) timeFraction = 1; // текущее состояние анимации var progress = options.timing(timeFraction) options.draw(progress); if (timeFraction < 1) { requestAnimationFrame(animate); } }); }

Основные параметры:

  • duration – длительность анимации в мс.
  • timing – функция, которая определяет состояние анимации каждый кадр. Получает часть времени от 0 до 1, возвращает завершенность анимации от 0 до 1.
  • draw – функция, которая отрисовывает состояние анимации от 0 до 1.

Эту функцию можно улучшить, например добавить коллбэк complete для вызова в конце анимации.

Мы рассмотрели ряд примеров для timing и трансформации easeOut, easeInOut, которые позволяют их разнообразить. В отличие от мы не ограничены кривыми Безье, можно реализовать всё, что угодно.

Это же относится и к функции draw.

Такая реализация анимации имеет три основных области применения:

  • Нестандартные задачи и требования, не укладывающиеся в рамки CSS.
  • Поддержка IE9-.
  • Графика, рисование на canvas.
Задания

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

Flash постепенно сошел с дистанции, и талантливые аниматоры заставили HTML5 делать вещи, которые он никогда не делал раньше. Им нужны были более эффективные инструменты для сложных эффектов и первоклассной производительности.

jQuery просто не был предназначен для этого. Браузеры повзрослели и начали предлагать решения.

Наиболее широко известным решением стали CSS анимации . CSS анимация как любимец IT-индустрии, на протяжении нескольких лет нескончаемо обсуждалась на конференциях, где такие фразы, как «аппаратное ускорение » и «дружественность мобильным устройствам » ласкали слух аудитории.

Анимации на основе JavaScript рассматривались как устаревшие и «неприличные ». Но так ли это на самом деле?

Будучи тем, кто очарован (на грани одержимости, фактически) анимацией и производительностью, я с нетерпением бросился в объятия CSS. И не успел я далеко продвинуться, как обнаружилось множество серьезных проблем, о которых никто не рассказывал. Я был в шоке.

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

Отсутствие независимого контроля масштаба/поворота/позиции

Анимация масштаба, поворота и позиции элемента является чрезвычайно распространенной. В CSS все эти параметры запиханы в свойство transform, что делает невозможным создание отдельной анимации для каждого параметра одного элемента.

Например, что если вы хотите сделать анимацию поворота и масштаба независимо друг от друга, с разным расчетом времени выполнения и функциями смягчения?

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

HTML:

Обратите внимание, что масштаб, поворот и позиция могут быть анимированы независимо друг от друга, с использованием разных функций смягчения и частично перекрывающимся временем начала/конца (что невозможно с CSS-анимациями).

Независимые преобразования Spin rotation Spin rotationX Spin rotationY wander (position)

CSS:

body { background-color:black; margin: 0; padding: 0; font-family: Signika Negative, sans-serif; font-weight: 300; } html, body { height: 100%; } #demo { display:table; width:100%; height:100%; } #field { position:relative; display:table-cell; height: 100%; overflow:hidden; text-align: center; vertical-align: middle; } #box { color: black; font-size:24px; padding: 10px 16px; border: 2px solid black; background: #9af600; background: linear-gradient(to bottom, #9af600 0%,#71B200 100%); display:inline-block; border-radius: 10px; } #field p { position: absolute; color: #999; top: 0px; padding: 0px 20px; text-align: left; z-index: -1000; } #controls { position:absolute; color: #999; width: 100%; bottom: 20px; text-align: center; } button { margin: 2px; }

JS:

var $box = $("#box"), $field = $("#field"), rotation = 0, rotationX = 0, rotationY = 0, wanderTween, ignoreRollovers; //применяем перспективу к контейнеру, чтобы мы могли видеть трехмерность. TweenLite.set($field, {perspective: 500}); //смещаем источник по оси z, чтобы сделать вращение более интересным. TweenLite.set($box, {transformOrigin:"center center -150px"}); //заставляем прямоугольник пульсировать с помощью scaleX и scaleY TweenMax.to($box, 1.2, {scaleX:0.8, scaleY:0.8, force3D:true, yoyo:true, repeat:-1, ease:Power1.easeInOut}); //при наведении, переворачиваем прямоугольник, но чтобы избежать чрезмерного //вращения, мы уменьшим чувствительность к наведению в течение первой секунды //анимации. $box.hover(function() { if (!ignoreRollovers) { rotation += 360; ignoreRollovers = true; TweenLite.to($box, 2, {rotation:rotation, ease:Elastic.easeOut}); TweenLite.delayedCall(1, function() { ignoreRollovers = false; }); } }, function() {}); $("#rotation").click(function() { rotation += 360; TweenLite.to($box, 2, {rotation:rotation, ease:Elastic.easeOut}); }); $("#rotationX").click(function() { rotationX += 360; TweenLite.to($box, 2, {rotationX:rotationX, ease:Power2.easeOut}); }); $("#rotationY").click(function() { rotationY += 360; TweenLite.to($box, 2, {rotationY:rotationY, ease:Power1.easeInOut}); }); $("#move").click(function() { if (wanderTween) { wanderTween.kill(); wanderTween = null; TweenLite.to($box, 0.5, {x:0, y:0}); } else { wander(); } }); //случайно выбираем место на экране и запускаем там анимацию, затем повторяем это //снова и снова. function wander() { var x = (($field.width() - $box.width()) / 2) * (Math.random() * 1.8 - 0.9), y = (($field.height() - $box.height()) / 2) * (Math.random() * 1.4 - 0.7); wanderTween = TweenLite.to($box, 2.5, {x:x, y:y, ease:Power1.easeInOut, onComplete:wander}); }

По моему мнению, это явно слабая сторона CSS, но если вы создаете более простые анимации, которые задействуют преобразования целиком в любой момент времени, тогда этот момент не составит для вас проблему.

Производительность

Большинство сравнений в Интернете противопоставляют CSS анимации и библиотеку jQuery , так как она очень распространена (как если «JavaScript » и «jQuery » были бы синонимами), но широко известно, что jQuery весьма медленная в плане производительности анимации.

Более новая библиотека GSAP также основана на JavaScript, но она буквально в 20 раз быстрее, чем jQuery.. Поэтому одной из причин, из-за которой JavaScript получила плохую репутацию, является, как я его называю, «фактор jQuery ».

Наиболее часто приводимый довод за использование CSS для анимации – это «аппаратное ускорение ». Звучит аппетитно, правда?

Давайте разобьем его на две части:

Вовлечение графического процессора

Графический процессор (GPU) сильно оптимизирован для таких задач, как перемещение пикселей, применение прозрачности и матриц преобразования, поэтому современные браузеры стараются переложить такие задачи с центрального процессора (CPU) на GPU.

Секрет заключается в том, чтобы выделить анимированные элементы в собственные GPU слои, потому что как только слой создан (при условии, что его исходные пикселы не меняются), для GPU не составляет никакого труда перемещать эти пиксели и комбинировать их вместе.

Вместо вычисления каждого отдельного пиксела 60 раз в секунду, GPU может сохранить группы пикселей (как слои) и просто сказать: «Смещаем эту группу на 10 пикселей и на 5 пикселей вниз » (или что-то в этом роде).

Примечание: неразумно выделять каждому элементу свой собственный слой, потому что GPU имеет ограниченную видеопамять. Если вы выйдите за ее пределы, все резко замедлится.

Объявление анимаций в CSS позволяет браузеру определить, какие элементы должны получить слои GPU, и распределить их соответствующим образом. Отлично!

Но знаете ли вы, что с JavaScript вы тоже можете это делать? Установка преобразования с 3D характеристикой (например, translate3d() или matrix3d() ) заставляет браузер создать GPU слой для такого элемента. Так что увеличение скорости с помощью GPU работает не только для CSS анимаций – анимации на JavaScript тоже могут извлечь из этого выгоду!

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

Поэтому не думайте, что если вы создаете анимацию с помощью CSS, то все волшебным образом становится ускоренным GPU. Это просто не соответствует действительности.

Перераспределение вычислений на другой поток

Другая часть «аппаратного ускорения » связана с возможностью использования разных потоков CPU для вычислений, относящихся к анимации. Опять же, это звучит здорово в теории, но не проходит без затрат, и разработчики часто переоценивают получаемые преимущества.

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

Поэтому снова, преобразования и прозрачность – основные получатели выгоды. При ответвлении потоков появляются накладные расходы, связанные с управлением этим процессом.

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

Например, если 98% работы в течение определенной анимации это рендеринг графики и макет документа, а 2% — выяснение новых значений позиции/поворота/прозрачности/еще чего-то, даже вычисляя их в 10 раз быстрее, в целом вы увидите примерно всего 1% прироста скорости.

Сравнение производительности

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

Задайте большое количество точек и посмотрите сравнение jQuery , GSAP и Zepto .

Поскольку Zepto использует CSS трансформации для всех анимаций, его производительность должна быть самой лучшей, так?

HTML:

  • Engine: jQuery (JS) GSAP (JS) Zepto (CSS)
  • Properties: top, left, width, height transform:translate(...) scale(...)
  • Dots: 25 50 100 200 300 400 500 750 1000 1250 1500 2000 2500 3000
  • START
  • HTML5 тестирование скорости анимации

    Сравните производительность анимаций на основе jQuery, GSAP (GreenSock Animation Platform), и CSS трансформаций (которые используются в Zepto). Выберите параметры выше и нажмите "START". Увеличивайте количество точек, пока не увидите прерывистые движения или скопления/окружности. Также обратите внимание, какие свойства анимируются быстрее: "top", "left", "width" и "height" или "transform: translate(...) scale(...)". Вы, возможно, будете удивлены.

    CSS:

    body { background-color: black; margin:0; padding:0; color:#eee; font-family: Signika Negative, sans-serif; font-weight: 300; font-size: 1.15em; user-select:none; -webkit-user-select:none; } html, body { height: 100%; overflow:hidden; } h1 { font-weight: 400; font-size:2em; line-height:1em; margin-bottom:0.1em; color: white; } a, a:hover, a:visited { color:#71B200; } #controls { display:table-row; background-color:#555; background: linear-gradient(to bottom, #777 0%,#444 100%); padding:10px 10px 10px 5px; z-index:1000; } #controls form li { display:table-cell; padding:12px 6px 10px 6px; vertical-align:middle; text-shadow: 1px 1px 1px #000; } #instructions { width:82%; margin-left:8%; padding-top:1em; line-height: 1.5em; color:#ccc; } #demo { display:table; width:100%; height:100%; } #field { position:relative; display:table-cell; width:100%; height: 100%; overflow:hidden; z-index:-100; border-top: 1px solid #777; } #start { color: black; border-radius: 6px; padding: 5px 18px; border: 2px solid black; background: #9af600; background: linear-gradient(to bottom, #9af600 0%,#71B200 100%); cursor: pointer; text-shadow: none; font-weight: 400; }

    JS:

    var $start = $("#start"), $dotQtyInput = $("#dotQuantity"), $engineInput = $("#engine"), $propertiesInput = $("#properties"), $instructions = $("#instructions"), $field = $("#field"), $window = $(window), $inputs = $("select"), inProgress = false, tests = {}, duration, radius, centerX, centerY, dots, rawDots, currentTest, startingCSS; /** * Целью этого теста является сравнение того, как различные инструменты для создания анимаций работают при нагрузке, получая относительно распространенные анимационные задачи и выполняя их в большом количестве, чтобы увидеть чистую производительность. Целью не является выяснение самого эффективного способа перемещения точек в шаблоне звездного поля. * * Одинаковый код использован во всем за исключением самих анимаций. Каждый тест в объекте “test” имеет 4 свойства: * * - milliseconds – имеет значение true, если продолжительность должна быть определенна в миллисекундах * * - wrapDot – когда каждая точка создается, она должна быть передана в метод wrapDot() и то, что возвращается функцией, сохраняется в массиве точек для анимации. Это полезно для повышения производительности таких вещей, как jQuery, потому что вместо передачи DOM элемента в метод tween() (который потребует jQuery запросить DOM и обернуть элемент в специфичный для движка объект до вызова animate()) может быть использован естественный объект. Проще говоря, это позволит кэшировать обертывание точек для повышения производительности. * * - tween – это ядро всего теста. tween() вызывается для каждой точки, точка передается в качестве параметра. Функция tween() должна устанавливать cssText точки в значение startingCSS (которое просто помещает точку в середину экрана и устанавливает значение для ее ширины и высоты в 1 пиксель) и затем после произвольной задержки длительностью от 0 до времени выполнения анимации, функция должна анимировать точку под случайным углом, изменяя либо значения left/top, либо преобразования translate(), и устанавливать размер в 32 пикселя в ширину и высоту, используя либо width/height, либо scale(). Затем, после завершения анимации, метод tween() должен быть вызван снова для этой же точки. Так что та же самая точка просто будет постоянно двигаться от центра под случайным углом и с произвольным временем задержки. * * - stop – эта функция вызывается, когда пользователь останавливает тест. Точка передается в качестве параметра. Функция должна тотчас остановить/уничтожить анимацию (или анимации), запущенные для этой точки (или для всех точек – это тоже нормально). * * - nativeSize – имеет значение true, если начальные width/height изображения должны быть в натуральную величину (обычно необходимо для преобразований, но не когда мы анимируем width/height). * *Я не претендую на звание эксперта в разнообразных движках для анимации, поэтому, если есть пути оптимизации, которые можно применить, чтобы улучшить выполнение теста, пожалуйста, дайте мне знать. Я пытался сделать все максимально беспристрастно. **/ //стандартный jQuery normal (top/left/width/height) tests.jquery_normal = { milliseconds:true, wrapDot:function(dot) { return jQuery(dot); //заворачиваем точку в объект jQuery для повышения производительности (таким образом, нам не нужно запрашивать DOM каждый раз, когда мы анимируем – мы можем просто вызвать animate() прямо для объекта jQuery) }, tween:function(dot) { var angle = Math.random() * Math.PI * 2; dot.style.cssText = startingCSS; dot.delay(Math.random() * duration).animate({left:Math.cos(angle) * radius + centerX, top:Math.sin(angle) * radius + centerY, width:32, height:32}, duration, "cubicIn", function() { tests.jquery_normal.tween(dot) }); }, stop:function(dot) { dot.stop(true); }, nativeSize:false }; //стандартный GSAP (top/left/width/height) tests.gsap_normal = { milliseconds:false, wrapDot:function(dot) { return dot; //нет необходимости в обертывании }, tween:function(dot) { var angle = Math.random() * Math.PI * 2; dot.style.cssText = startingCSS; TweenLite.to(dot, duration, {css:{left:Math.cos(angle) * radius + centerX, top:Math.sin(angle) * radius + centerY, width:32, height:32}, delay:Math.random() * duration, ease:Cubic.easeIn, overwrite:"none", onComplete:tests.gsap_normal.tween, onCompleteParams:}); }, stop:function(dot) { TweenLite.killTweensOf(dot); }, nativeSize:false }; //GSAP преобразования (translate()/scale()) tests.gsap_transforms = { milliseconds:false, wrapDot:function(dot) { return dot; // нет необходимости в обертывании }, tween:function(dot) { var angle = Math.random() * Math.PI * 2; TweenLite.set(dot, {css:{x:0, y:0, scale:0.06}, overwrite:"none"}); TweenLite.to(dot, duration, {css:{x:(Math.cos(angle) * radius), y:(Math.sin(angle) * radius), scaleX:2, scaleY:2}, delay:Math.random() * duration, ease:Cubic.easeIn, overwrite:"none", onComplete:tests.gsap_transforms.tween, onCompleteParams:}); }, stop:function(dot) { TweenLite.killTweensOf(dot); }, nativeSize:true }; //стандартный Zepto (top/left/width/height) tests.zepto_normal = { milliseconds:true, wrapDot:function(dot) { return Zepto(dot); // заворачиваем точку в объект jQuery для повышения производительности (таким образом, нам не нужно запрашивать DOM каждый раз, когда мы анимируем – мы можем просто вызвать animate() прямо для объекта jQuery) }, tween:function(dot) { var angle = Math.random() * Math.PI * 2; dot.style.cssText = startingCSS; //Функция задержки в Zepto под давлением работает УЖАСНО, поэтому вместо нее мы используем функцию setTimeout() для повышения производительности. setTimeout(function() { if (!dot.isKilled) { //Zepto не имеет функции, которая позволила бы нам уничтожить запущенные анимации, поэтому мы просто устанавливаем наше собственное свойство isKilled в значение true, когда предполагается, что анимация прекращена и затем останавливаем рекурсию, что дает нам необходимый эффект. dot.animate({left:Math.cos(angle) * radius + centerX, top:Math.sin(angle) * radius + centerY, width:32, height:32}, duration, "cubic-bezier(0.550, 0.055, 0.675, 0.190)", function() { tests.zepto_normal.tween(dot) }); } }, duration * Math.random()); }, stop:function(dot) { dot.isKilled = true; }, nativeSize:false }; //Zepto преобразования (translate()/scale()) tests.zepto_transforms = { milliseconds:true, wrapDot:function(dot) { return Zepto(dot); // заворачиваем точку в объект jQuery для повышения производительности (таким образом, нам не нужно запрашивать DOM каждый раз, когда мы анимируем – мы можем просто вызвать animate() прямо для объекта jQuery) }, tween:function(dot) { // Мне не удалось задать функцию css() в Zepto (она не выполнилась), поэтому пришлось использовать вызов animate() нулевой длительности. Хотя это справедливо, потому что для TweenLite мы, на самом деле, тоже делаем анимацию нулевой длительности. dot.animate({translateX:"0px", translateY:"0px", rotateY:"0rad", rotateX:"0rad", scale:"0.06,0.06"},0); // Функция задержки в Zepto под давлением работает УЖАСНО, поэтому вместо нее мы используем функцию setTimeout() для повышения производительности. setTimeout(function() { if (!dot.isKilled) { //Zepto не имеет функции, которая позволила бы нам уничтожить запущенные анимации, поэтому мы просто устанавливаем наше собственное свойство isKilled в значение true, когда предполагается, что анимация прекращена и затем останавливаем рекурсию, что дает нам необходимый эффект. var angle = Math.random() * Math.PI * 2; dot.animate({translateX:(Math.cos(angle) * radius) + "px", translateY:(Math.sin(angle) * radius) + "px", scale:"2,2", delay:duration * Math.random()}, duration, "cubic-bezier(0.550, 0.055, 0.675, 0.190)", function() { tests.zepto_transforms.tween(dot); }); } }, duration * Math.random()); }, stop:function(dot) { dot.isKilled = true; }, nativeSize:true }; function toggleTest() { var i, size; inProgress = !inProgress; if (inProgress) { $inputs.prop("disabled", true); $field.css({pointerEvents:"none"}); //улучшаем производительность – игнорируем события указателя во время анимации $start.html(" STOP "); $start.css({background:"#C00"}); TweenLite.to($instructions, 0.7, {autoAlpha:0, overwrite:"all"}); currentTest = tests[$engineInput.val() + "_" + $propertiesInput.val()]; size = (currentTest.nativeSize ? "16px" : "1px"); centerX = $field.width() / 2; centerY = $field.height() / 2; startingCSS = "position:absolute; left:" + centerX + "px; top:" + centerY + "px; width:" + size + "; height:" + size + ";"; radius = Math.sqrt(centerX * centerX + centerY * centerY); duration = currentTest.milliseconds ? 750: 0.75; //ждем миллисекунду прежде чем создавать точки и начать анимацию, поэтому пользовательский интерфейс воспроизводится вперед (превращая кнопку «старт» в «стоп»), в противном случае пользователи могли бы запутаться во время длительной паузы, когда выбран Zepto и преобразования, в связи с тем, что возможно, это займет некоторое время, пока браузер поместит все точки на их собственные слои. setTimeout(function() { createDots(); i = dots.length; while (--i > -1) { currentTest.tween(dots[i]); } }, 1); } else { $start.html(" START "); $start.css({backgroundColor:"#9af600", background: "linear-gradient(to bottom, #9af600 0%,#71B200 100%"}); TweenLite.to($instructions, 0.7, {autoAlpha:1, delay:0.2}); $inputs.prop("disabled", false); calibrateInputs(); $field.css({pointerEvents:"auto"}); //останавливаем анимацию и удаляем точки. i = dots.length; while (--i > -1) { currentTest.stop(dots[i]); $field.removeChild(rawDots[i]); //удаляет точку (или точки) } dots = null; rawDots = null; } } function createDots() { var i = parseInt($dotQtyInput.val()), dot; dots = ; rawDots = ; while (--i > -1) { dot = document.createElement("img"); dot.src = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/16327/dot.png"; dot.width = 1; dot.height = 1; dot.id = "dot" + i; dot.style.cssText = startingCSS; $field.appendChild(dot); rawDots.push(dot); dots.push(currentTest.wrapDot(dot)); } } function calibrateInputs(e) { if ($engineInput.val() === "jquery") { //jQuery не может анимировать преобразования без стороннего плагина, поэтому отключаем эту опцию $propertiesInput.selectedIndex = 0; $propertiesInput.prop("disabled", true); } else { $propertiesInput.prop("disabled", false); } } $start.click(toggleTest); $inputs.change(calibrateInputs); jQuery.easing.cubicIn = $.easing.cubicIn = function(p, n, firstNum, diff) { //нам нужно добавить стандартную функцию смягчения CubicIn в jQuery return firstNum + p * p * p * diff; } jQuery.fx.interval = 16; //гарантирует, что jQuery обновляет примерно 60 кадров в секунду, как GSAP и другие, чтобы показать более равный/беспристрастный результат. $propertiesInput.prop("disabled", true);

    Результаты подтверждают то, что широко отмечается в Интернете – CSS анимации значительно быстрее, чем jQuery.

    Однако на большинстве устройств и браузеров, в которых я проводил тестирование, GSAP на основе JavaScript имеет даже лучшую производительность, чем CSS анимации (с большим отрывом в некоторых случаях, как например, на Microsoft Surface RT где библиотека GSAP была по крайней мере в 5 раз быстрее, чем CSS трансформации, созданные с помощью Zepto , и на iPad 3 с iOS7 преобразования были гораздо быстрее, когда выполнялись с помощью GSAP вместо CSS трансформаций):

    Анимированные свойства Лучше с JavaScript Лучше с CSS
    top, left, width, height Windows Surface RT, iPhone 5s (iOS7), iPad 3 (iOS 6), iPad 3 (iOS7), Samsung Galaxy Tab 2, Chrome, Firefox, Safari, Opera, Kindle Fire HD, IE11
    преобразования (сдвиг/масштаб) Windows Surface RT, iPhone 5s (iOS7), iPad 3 (iOS7), Samsung Galaxy Tab 2, Firefox, Opera, IE11 iPad 3 (iOS6), Safari, Chrome

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

    Хотя вы легко можете оценить относительную производительность, увеличивая количество точек, переключаясь между движками и наблюдая, как происходит выполнение анимации (плавность движения, равномерность промежутков времени, распределение точек и т.п.). В конце концов, главная цель состоит в том, чтобы получить презентабельные анимации.

    Интересно отметить:

    • При анимации свойств top/left/width/height (влияющих на поток документа) JavaScript был быстрее по всем направлениям (GSAP, не jQuery);
    • Некоторые устройства оказались хорошо оптимизированными для преобразований, в то время как другие лучше обрабатывали анимации со свойствами top/left/width/height. В частности, более старая iOS6 выполняла преобразования на основе CSS намного лучше, а новая iOS7 сменила приоритеты, и сейчас они выполняются значительно медленнее;
    • Существует значительная задержка в первоначальном запуске анимации на основе CSS, так как браузер вычислят слои и загружает данные в GPU. Это также применимо и к трехмерным преобразованиям на JavaScript, поэтому «ускорение с GPU» не проходит без потерь;
    • При сильных нагрузках CSS трансформации с большей вероятностью распыляются в полосы или кольца (это связано с проблемами синхронизации и планирования, возможно, из-за управления в другом потоке);
    • В некоторых браузерах (например, Google Chrome), когда в анимации было задействовано очень большое количество точек, совершенно отсутствовало постепенное затухание текста, но происходило это только при использовании CSS анимаций!

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

    Хотя будем надеяться, что это изменится. Как бы то ни было, в большинстве реальных проектов вы никогда бы не заметили разницу в производительности.

    Я призываю вас провести собственное тестирование, чтобы посмотреть какая технология обеспечит плавную анимацию в конкретном проекте(ах).

    Не покупайтесь на миф, что CSS анимации всегда быстрее, и не считайте, что тест на скорость, приведенный выше, отражает то, что вы увидите в вашем приложении. Тестируйте, тестируйте и еще раз тестируйте.

    Элементы управления выполнением и события

    Некоторые браузеры позволяют остановить или перезапустить анимацию с ключевыми кадрами CSS, но на этом – все.

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

    JavaScript, напротив, предоставляет большие возможности для контроля, как можно увидеть в примере ниже.

    HTML:

    Невозможно с CSS анимациями. play reverse restart Speed: slow normal fast

    CSS:

    body { font-family: Signika Negative, sans-serif; font-weight: 300; color: white; background-color: black; text-align: center; } #demo { text-align: center; margin-top:20px; } #bg { background-color:#000; position:relative; overflow:hidden; display:inline-block; width:500px; height:70px; border-radius: 8px; border: 2px solid #777; } #text { position:absolute; text-align:center; width:500px; height:70px; line-height:68px; font-size: 28px; } #text span { -webkit-font-smoothing: antialiased; -moz-font-smoothing:antialiased; position:relative; display:inline-block; color:#FFF; } #slider { display: inline-block; width: 500px; height:12px; margin:8px 0px 8px 6px; } #controls button { width: 80px; } #controls input { display:inline; padding:2px; margin:2px; }

    JS:

    var $text = $("#text"), $pause = $("#pause"), $reverse = $("#reverse"), $restart = $("#restart"), $speed = $("input"), $slider = $("#slider"), //"tl" является шкалой, к которой мы добавим нашу анимацию. //Тогда мы сможем легко контролировать всю последовательность выполнения //анимации как один объект. tl = new TimelineLite({onUpdate:updateSlider, onComplete:onComplete, onReverseComplete:onComplete, paused:true}); function updateSlider() { $slider.slider("value", tl.progress() * 100); } function onComplete() { tl.pause(); $pause.html("play"); } //делаем простое разделение текста, чтобы мы могли применить анимацию к каждому //символу (не нужны расширенные функции SplitText, поэтому просто используем split() и //join()) $text.html("" + $text.html().split("").join("").split("").join("") + ""); //устанавливаем перспективу на контейнере TweenLite.set($text, {perspective:500}); //вся анимация создается в одной строке: tl.staggerTo($("#text span"), 4, {transformOrigin:"50% 50% -30px", rotationY:-360, rotationX:360, rotation:360, ease:Elastic.easeInOut}, 0.02); //управление ползунком и кнопкой $slider.slider({ range: false, min: 0, max: 100, step:.1, slide: function (event, ui) { tl.progress(ui.value / 100).pause(); $pause.html("play"); } }); $pause.click(function() { if (tl.paused()) { if (tl.progress() === 1 || (tl.progress() === 0 && tl.reversed())) { tl.restart(); } else { tl.resume(); } $pause.html("pause"); } else { tl.pause(); $pause.html("resume"); } }); $reverse.click(function() { if (tl.progress() === 0) { if (tl.reversed()) { tl.play(); } else { tl.reverse(0); } $pause.html("pause"); } else { tl.reversed(!tl.reversed()).resume(); $pause.html("pause"); } }); $restart.click(function(){ tl.restart(); $pause.html("pause"); }); $speed.change(function(v, val) { tl.timeScale(parseFloat($(this).val())); if (tl.progress() === 1) { tl.restart(); $pause.html("pause"); } else if (tl.paused()) { tl.resume(); $pause.html("pause"); } });

    Современная анимация очень сильно связана с интерактивностью, поэтому невероятно полезно иметь возможность выполнять анимацию от изменяемых начальных значений до изменяемых конечных (например, зависящих от того, в каком месте пользователь щелкнет мышью) или изменить что-то «на лету », но декларативные анимации на основе CSS не позволяют сделать это.

    Рабочий процесс

    Для простых переходов между двумя состояниями (например, перевороты или раскрытие меню, и т.п.) отлично подходят CSS трансформации.

    Однако, для последовательности событий вам, как правило, потребуется использовать анимации CSS с ключевыми кадрами, в которых обязательно нужно определять селекторы в виде процентов, например:

    CSS:

    @keyframes myAnimation { 0% { opacity: 0; transform: translate(0, 0); } 30% { opacity: 1; transform: translate(0, 0); } 60% { transform: translate(100px, 0); } 100% { transform: translate(100px, 100px); } } #box { animation: myAnimation 2.75s; }

    Но когда вы создаете анимацию, разве вы не ориентируетесь с помощью временных интервалов, а не процентов? Например, «увеличить прозрачность в течение 1 секунды, потом сдвигать вправо в течение 0,75 секунды, и затем на оставшейся секунде сделать скачок вниз ».

    Что произойдет, если вы потратите несколько часов на вычисление сложной последовательности в процентах, а клиент затем скажет: «Сделайте эту часть в середине на 3 секунды дольше »? Уфф, вам придется пересчитывать ВСЕ проценты!

    Обычно построение анимаций включает в себя большое количество экспериментов, особенно со временем и функциями смягчения.

    Это актуально там, где метод seek() оказался бы достаточно полезным. Представьте себе создание 60-секундной анимации кусочек за кусочком, а затем оттачивание заключительных 5 секунд: вам пришлось бы просиживать первые 55 секунд каждый раз, когда вы хотите посмотреть результат ваших корректировок в последних частях.

    Фу! С помощью метода seek() вы могли бы в процессе работы просто сослаться на определенное место анимации, чтобы попасть в ту часть, над которой работаете, а затем удалить его, когда закончите. Это значительная экономия времени.

    Широкое распространение получает создание анимации для объектов на основе canvas и других объектов сторонних библиотек, но, к сожалению, CSS анимации предназначены только для DOM элементов.

    Это значит, что если вы вложили много времени и энергии в CSS анимации, их нельзя будет перенести в другие типы проектов.

    Вы должны будете изменить набор инструментов для анимации.

    Существует еще несколько удобных инструментов, относящихся к рабочему процессу, которые отсутствуют в CSS анимациях:

    • Относительные величины . Например, «сделать поворот на 30 градусов больше » или «переместить элемент вниз на 100 пикселей от места, где он находился, когда началась анимация »;
    • Вложенность . Представьте себе возможность создавать анимации, которые могут быть вложены в другую анимацию, которая сама может вложенной и т.д. Вообразите управление главной анимацией, в то время как все остальное остается полностью синхронизировано. Такая структура способствовала бы модульному коду, который намного легче создавать и поддерживать;
    • Отчет о ходе выполнения . Завершена ли конкретная анимация? Если нет, то на какой точно стадии выполнения она находится?
    • Целевые ликвидации . Иногда невероятно полезно ликвидировать все анимации, которые влияют на масштаб элемента (или любое другое свойство по вашему желанию), разрешив при этом все остальные;
    • Краткий код . CSS анимации на основе ключевых кадров отличаются многословностью, даже если не учитывать необходимость всех избыточных вендорно-префиксных версий. Любой, кто пытался создать что-то даже средней сложности, подтвердит тот факт, что CSS анимации быстро становятся громоздкими и неуклюжими. На самом деле, реальный объем CSS кода, необходимого для выполнения задач по анимации, может превышать вес библиотеки JavaScript (которую легче поместить в кэш и использовать повторно во многих анимациях).
    Ограниченные эффекты

    Вы не сможете сделать ничего из нижеследующего списка при помощи CSS анимации:

    • Анимация вдоль кривой (например, кривой Безье);
    • Использование интересных функций смягчения, таких как упругое, пружинистое или грубое смягчение. Существует опция cubic-bezier() , но она допускает только две ключевые точки, что сильно ограничивает возможности;
    • Движения по физическим законам. Например, плавное перемещение на основе инерции и легкое возвращение назад, реализованное в этом Draggable demo ;
    • Анимация позиции прокрутки;
    • Направленный поворот (например, «повернуть ровно на 270 градусов в кратчайшем направлении, по часовой или против часовой стрелки »);
    • Анимация атрибутов.
    Совместимость

    Анимации на основе CSS не работают в IE9 и более ранних версиях браузера. Большинство из нас ненавидят обеспечивать поддержку устаревших браузеров (особенно IE), но реальность такова, что у некоторых из нас есть клиенты, которые требуют сделать это.

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

    Заключение

    Плохи ли CSS анимации? Определенно нет. Фактически, они отлично подходят для простых переходов между состояниями (например, переворачивания), когда не требуется совместимость с устаревшими браузерами.

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

    Однако анимации на основе JavaScript предоставляют гораздо большую гибкость, более комфортный процесс разработки для сложных анимаций и роскошную возможность интерактивного взаимодействия, и часто анимации на JavaScript выполняются также быстро (или даже быстрее) как и анимации на основе CSS, несмотря на то, что вы, возможно, слышали.

    Я могу понять, почему CSS анимации были такими привлекательными по сравнению с jQuery.animate() . Кто в здравом уме не ухватился бы за возможность получить 10-кратный прирост производительности?

    Теперь нет необходимости выбирать между jQuery и CSS анимациями, инструменты на основе JavaScript, такие как GSAP , открывают совершенно новые возможности и сокращают разрыв в производительности.

    Это статья – не о GSAP или какой-либо определенной библиотеке; мораль статьи в том, что анимации на основе JavaScript не заслуживают плохой репутации. Фактически, JavaScript является единственным вариантом для по-настоящему надежной и гибкой системы анимации.

    Плюс, я хотел пролить немного света на некоторые стороны CSS анимаций, вызывающие разочарование (о которых никто не говорит), так что теперь вы можете принять более обоснованное решение о том, как создавать анимации.

    Решит ли спецификация Web Animations существующие проблемы?

    Консорциум W3C работает над новой спецификацией под названием «Web Animations » (веб-анимации), целью которой является решение многих недостатков в CSS анимациях и трансформациях, обеспечение лучшего контроля выполнения и дополнительных функций.

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

    Хотя эту уже совсем другая история. Нам необходимо подождать и посмотреть, как все сложится. Есть, несомненно, умные ребята, работающие над этой спецификацией.

    Данная публикация представляет собой перевод статьи «Myth Busting: CSS Animations vs. JavaScript » , подготовленной дружной командой проекта

    Под термином «анимация» мы чаще всего понимаем мультипликационные фильмы - с детства любимые нами «мультики». Но если заглянуть в толковый словарь, то мы узнаем, что в переводе с французского оно означает «оживление», «одушевление». И вот тут-то оказывается, что это значение удивительно точно подходит не только к киноиндустрии, но и к веб-технологиям.

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

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

    Говоря о технологиях, обеспечивающих использование анимации в веб-страницах , можно выделить несколько но, пожалуй, ни одна из них не является настолько мощной как . Еще несколько лет назад технология Flash-анимации была грозным конкурентом и очень популярной. Но сейчас, похоже, ее лучшие годы позади и она постепенно вытесняется со страниц сайтов более мощными и гибкими Java-скриптами. И если вы решили всерьез использовать анимацию на своем сайте , то ставку следует делать именно на JavaScript. А чтобы сделать разумный выбор библиотеки я и сделал сегодняшний обзор.

    Dynamics.jsНачну я, пожалуй, с Dynamics.js . Это серьезная и мощная библиотека, позволяющая создавать физически достоверную анимацию (как, например, гармонические затухающие колебания точки на главной странице сайта). Библиотека способна управлять и свойствами любого DOM-элемента. Dynamics.js используется для создания меню , кнопок, индикаторов процесса, маркеров. При этом доступны самые разнообразные параметры, такие как частота, декремент затухания, параметры, характеризующие упругость или продолжительность процесса и т.п.

    Cta.jsНебольшая по объему библиотека cta.js предназначена для создания на странице анимационных эффектов типа «действие-эффект», т.е. наведение или нажатие указателем мыши на объект приводит к определенному эффекту. Очень удобно использовать при разработке плиточных интерфейсов, когда нажатие на элемент приводит к его раскрытию в виде модального окна, на всю страницу, или в виде боковой слайд-панели .

    Beep.jsИнтересная библиотека, использующая WebAudio API для создания на странице музыкального синтезатора. Может найти применение при разработке онлайн-учебника по музыке или в качестве забавной игрушки.

    Rainyday.jsНевероятно красивый эффект дождя с каплями разного размера, стекающими вниз. Правда, на мой взгляд, крупным каплям не хватает реалистичности (может той самой физики, которая присутствует в Dynamics.js?). Впрочем, имеющийся API позволяет создать собственные объекты и управлять их поведением, создавая еще более невероятные эффекты.

    Dom-Animator.jsDom-Animator.js - это так называемое «пасхальное яйцо» (easter egg). Производимый ею эффект не виден «невооруженным» глазом, т.е. тем кто просматривает страницу в обычном окне браузера. Но те, кто будет разбирать ваш код, увидят ее в консоли (если вы все еще не поняли о чем речь, то здесь есть видеоролик, из которого все станет понятно).

    FamousFamous - событийно-ориентированная JS-библиотека для создания современной анимации . Имеет мощное геометрическое ядро, позволяющее манипулировать различными 3D объектами - точечными и объемными - прикладывать к ним силы и ускорения, накладывать ограничения и контролировать соударения.

    Bounce.jsНеплохая JavaScript библиотека для создания впечатляющей анимации с использованием CSS. Позволяет применять к объектам различные виды движения, вращения, масштабирования и преобразования.

    Snabbt.jsЛегкая и быстрая библиотека, выдающая, по заверениям разработчиков, 60 fps даже на мобильных устройствах. При помощи матриц трансформирования CSS позволяет перемещать, вращать, масштабировать и производить другие манипуляции с объектами. Позволяет также применять к объектам специальные эффекты, привлекающие внимание, которые можно использовать при заполнении форм.

    RekapiRekapi позволяет использовать как CSS анимацию ключевых кадров (правило @keyframes), так и DOM анимацию при помощи JavaScript. Эта библиотека позволяет создавать довольно сложные динамические объекты, например круговые и линейные диаграммы, временные шкалы и другие элементы пользовательского интерфейса.

    ShiftyShifty - библиотека, содержащая все необходимое для полноценной анимации по ключевым кадрам (так называемый «твиннинг»), причем количество объектов может быть просто огромным. Это низкоуровневая библиотека, которую можно использовать как ядро для более высокоуровневых платформ или библиотек. Собственно, в качестве ядра вышеупомянутой Rekapi, используется как раз Shifty.

    Can’t make the #ChromeDevSummit this year? Catch all the content (and more!) on the livestream, or join your peers for a CDS Extended event at a hosted location nearby. To learn more, check out the Chrome Dev Summit 2019 website .

    CSS Versus JavaScript Animations

    Paul is a Design and Perf Advocate

    Evangelises Chrome and the mobile web in the Developer Relations team at Google.

    There are two primary ways to create animations on the web: with CSS and with JavaScript. Which one you choose really depends on the other dependencies of your project, and what kinds of effects you"re trying to achieve.

    TL;DR
    • Use CSS animations for simpler "one-shot" transitions, like toggling UI element states.
    • Use JavaScript animations when you want to have advanced effects like bouncing, stop, pause, rewind, or slow down.
    • If you choose to animate with JavaScript, use the Web Animations API or a modern framework that you"re comfortable with.

    Most basic animations can be created with either CSS or JavaScript, but the amount of effort and time differs (see also CSS vs JavaScript Performance). Each has its pros and cons, but these are good guidelines:

    • Use CSS when you have smaller, self-contained states for UI elements. CSS transitions and animations are ideal for bringing a navigation menu in from the side, or showing a tooltip. You may end up using JavaScript to control the states, but the animations themselves will be in your CSS.
    • Use JavaScript when you need significant control over your animations. The Web Animations API is the standards-based approach, available today in most modern browsers. This provides real objects, ideal for complex object-oriented applications. JavaScript is also useful when you need to stop, pause, slow down, or reverse your animations.
    • Use requestAnimationFrame directly when you want to orchestrate an entire scene by hand. This is an advanced JavaScript approach, but can be useful if you"re building a game or drawing to an HTML canvas.

    Alternatively, if you"re already using a JavaScript framework that includes animation functionality, such as via jQuery"s .animate() method or GreenSock"s TweenMax , then you may find it more convenient overall to stick with that for your animations.

    Animate with CSS

    Animating with CSS is the simplest way to get something moving on screen. This approach is described as declarative , because you specify what you"d like to happen.

    Below is some CSS that moves an element 100px in both the X and Y axes. It"s done by using a CSS transition that"s set to take 500ms . When the move class is added, the transform value is changed and the transition begins.

    Box { transform: translate(0, 0); transition: transform 500ms; } .box.move { transform: translate(100px, 100px); }

    Besides the transition"s duration, there are options for the easing , which is essentially how the animation feels. For more information about easing, see The Basics of Easing guide.

    If, as in the above snippet, you create separate CSS classes to manage your animations, you can then use JavaScript to toggle each animation on and off:

    Box.classList.add("move");

    Doing this provides a nice balance to your apps. You can focus on managing state with JavaScript, and simply set the appropriate classes on the target elements, leaving the browser to handle the animations. If you go down this route, you can listen to transitionend events on the element, but only if you’re able to forego support for older versions of Internet Explorer; version 10 was the first version to support these events. All other browsers have supported the event for some time.

    The JavaScript required to listen for the end of a transition looks like this:

    Var box = document.querySelector(".box"); box.addEventListener("transitionend", onTransitionEnd, false); function onTransitionEnd() { // Handle the transition finishing. }

    In addition to using CSS transitions, you can also use CSS animations, which allow you to have much more control over individual animation keyframes, durations, and iterations.

    Note: If you’re new to animations, keyframes are an old term from hand-drawn animations. Animators would create specific frames for a piece of action, called key frames, which would capture things like the most extreme part of some motion, and then they would set about drawing all the individual frames in between the keyframes. We have a similar process today with CSS animations, where we instruct the browser what values CSS properties need to have at given points, and it fills in the gaps.

    You can, for example, animate the box in the same way with transitions, but have it animate without any user interactions like clicking, and with infinite repetitions. You can also change multiple properties at the same time:

    Box { /* Choose the animation */ animation-name: movingBox; /* The animation’s duration */ animation-duration: 1300ms; /* The number of times we want the animation to run */ animation-iteration-count: infinite; /* Causes the animation to reverse on every odd iteration */ animation-direction: alternate; } @keyframes movingBox { 0% { transform: translate(0, 0); opacity: 0.3; } 25% { opacity: 0.9; } 50% { transform: translate(100px, 100px); opacity: 0.2; } 100% { transform: translate(30px, 30px); opacity: 0.8; } }

    With CSS animations you define the animation itself independently of the target element, and use the animation-name property to choose the required animation.

    If you want your CSS animations to work on older browsers, you will need to add vendor prefixes. Many tools can help you create the prefixed versions of the CSS you need, allowing you to write the unprefixed version in your source files.

    Animate with JavaScript and the Web Animations API

    Creating animations with JavaScript is, by comparison, more complex than writing CSS transitions or animations, but it typically provides developers significantly more power. You can use the Web Animations API to either animate specific CSS properties or build composable effect objects.

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

    Зачем анимировать элементы с помощью JavaScript

    При использовании CSS браузер выполняет за вас большую часть анимации. Для ее реализации разработчик просто определяете начальное и конечное состояние:

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

    Но это не является анимацией. Это значения свойств, заданные в определенные моменты анимации. Именно изменение значений между этими точками важно для работы анимации:

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

    С помощью JavaScript мы можем намного проще создавать сложную анимацию. Например, создать что-то вроде эффекта падающего снега без использования JavaScript будет трудно.

    Цикл анимации

    Цикл анимации — это функция, которая вызывается с повторяющимися интервалами. Она содержит код, отвечающий за изменение значений свойств. Чтобы было понятнее, взгляните на следующий пример (запустите его в отдельном окне ):

    Вы увидите синий круг, который неподвижен. Нажмите на кнопку « move » и обратите внимание, что произойдет. Круг сместится немного вправо. Нажмите на кнопку « move » снова , чтобы продолжить смещение.

    Чем быстрее вы нажимаете кнопку, тем быстрее будет двигаться круг. После нескольких нажатий круг исчезнет справа. Если вы продолжите нажимать кнопку, круг снова появится слева и продолжит движение вправо.

    Разметка и код примера:

    Animating in Code! body { background-color: #FFF; margin: 30px; margin-top: 10px; display: flex; align-items: center; justify-content: center; flex-direction: column; } #contentContainer { width: 550px; height: 350px; border: 5px black solid; overflow: hidden; background-color: #DFF2FF; display: flex; align-items: center; } #circle { width: 200px; height: 200px; background-color: #20A6FF; border-radius: 50%; } #move { background-color: gold; margin-top: 20px; font-size: 16px; font-weight: bold; border: 5px solid #333; outline: none; } #move:hover { background-color: coral; } #move:active { background-color: yellowgreen; } var circle = document.querySelector("#circle"); var button = document.querySelector("#move"); button.addEventListener("click", animate, false); var xPos = 0; function animate() { xPos += 10; circle.style.transform = `translate3d(${xPos}px, 0, 0)`; if (Math.abs(xPos) >= 900) { xPos = -500; } }

    Создайте новый HTML-документ и скопируйте в него приведенный выше код. Сохраните файл и откройте его в браузере. Вы должны увидеть тот же пример, который работает локально на вашем компьютере:

    В нашем примере функция animate вызывается каждый раз, когда для кнопки запускается событие click :

    var button = document.querySelector("#move"); button.addEventListener("click", animate, false);

    Код, окружающий функцию animate, выглядит следующим образом:

    var xPos = 0; function animate() { xPos += 10; circle.style.transform = `translate3d(${xPos}px, 0, 0)`; if (Math.abs(xPos) >= 900) { xPos = -500; } }

    Переменной xPos задано значение 0 . Каждый раз, когда вызывается функция animate, значение переменной увеличивается на 10 . Круг перемещается вправо благодаря следующей строке кода:

    circle.style.transform = `translate3d(${xPos}px, 0, 0)`;

    Мы используем функцию translate3d и задаем для горизонтального положения значение, сохраненное в переменной xPos. Когда значение xPos становится большим, и круг исчезает за пределы видимости, значение xPos сбрасывается до -500 :

    if (Math.abs(xPos) >= 900) { xPos = -500; }

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

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

    Цикл анимации — это функция, которая вызывается многократно благодаря обычной функции таймера requestAnimationFrame. Давайте изменим наш пример.

    Внутри тега внесите следующие изменения:

    var circle = document.querySelector("#circle"); var xPos = 0; function animate() { xPos += 10; circle.style.transform = `translate3d(${xPos}px, 0, 0)`; if (Math.abs(xPos) >= 900) { xPos = -500; } requestAnimationFrame(animate); } animate();

    Что мы сделали:

  • Вызвали функцию animate явно, чтобы она запускалась автоматически, без нажатия кнопки.
  • Поместили ниже функцию requestAnimationFrame,который будет вызывать функцию animate в каждом интервале обновления кадра.
  • Мы также удалили код, связанный с работой кнопки. Если бы мы просматривали анимацию в ее текущем состоянии, она выглядела бы следующим образом:

    При добавлении вызова requestAnimationFrame мы добавили функцию animate в захватывающий цикл анимации. Он отвечает за перемещение круга вправо на 10 пикселей при каждом обновлении кадра.

    Можно пойти еще дальше

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

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



    Понравилась статья? Поделиться с друзьями: