Создание анимации на базе JavaScript с помощью библиотеки Anime.js. Часть 3

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

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

Определение значений свойств

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

  • Им может быть безразмерная величина. В таком случае используются величины свойств по умолчанию для вычисления каких-либо значений свойств.
  • Значения могут определяться в виде строки, однако в таком случае она должна содержать по крайней мере одно числовое значение. Примеры значений строки: 10vh, 80% или 9.125turn.

Вместо абсолютного значения можно определить значение свойства относительно его текущего значения. Например, можно установить для translateY конечное значение на 150 px больше, чем оно есть, используя +=150px. Помните, что можно использовать добавление, умножение или вычитание только во время определения относительных значений.

Для анимации цветов нельзя использовать такие значения, как red, black или blue. При таких установках анимация не сработает вообще. Единственный правильный способ обозначения цветов при анимации — шестнадцатеричный код, RGB и HSL.

Как вы могли заметить, начальное значение для анимирования целевых элементов не было указано. Anime.js автоматически определяет начальное значение на основе CSS и значения по умолчанию этих свойств. Однако вы можете указать начальное значение, отличное от значения по умолчанию. Первый элемент в массиве означает начальное значение, а второй — конечное.

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

var uniqueTranslation = anime({
  targets: '.square',
  translateY: function(el, i) {
    return 50 * (i + 1);
  },
  autoplay: false
});
 
var randomScaling = anime({
  targets: '.square',
  scale: function(el, i) {
    return Math.random() * 1.5 + i / 10;
  },
  autoplay: false
});
 
var randomRotation = anime({
  targets: '.square',
  rotate: function() {
    return anime.random(-180, 180);
  },
  autoplay: false
});
 
var randomRadius = anime({
  targets: '.square',
  borderRadius: function(el) {
    return 20 + Math.random() * el.offsetWidth / 4;
  },
  autoplay: false
});
 
var randomAll = anime({
  targets: '.square',
  translateY: function(el, i) {
    return 50 + 50 * i;
  },
  scale: function(el, i) {
    return Math.random() * 1.5 + i / 10;
  },
  rotate: function() {
    return anime.random(-180, 180);
  },
  borderRadius: function(el) {
    return Math.random() * el.offsetWidth / 2;
  },
  duration: function() { return anime.random(1500, 2400); },
  delay: function() { return anime.random(0, 1000); },
  autoplay: false
});

Для установки значения свойства translateY в функции uniqueTranslation используется индекс элемента. Использование 50 * (i + 1) увеличивает значение translateY каждого элемента на 50 пикселей.

Анимация масштабирования (функция randomScaling в примере выше) также зависит от индекса элемента, но дополнительно рандомизируется встроенной функцией Math.random(). Она генерирует псевдослучайное число с плавающей точкой со значением меньше единицы. В таком случае элементы будут масштабироваться случайным образом, но часть кода i/10 немного увеличивает вероятность появления элементов, которые в конечном счёте будут иметь больший размер.

Для анимации вращения (функция randomRotation в примере выше) используется вспомогательная функция anime.random(a, b), формирующая случайные числа в диапазоне от -180 до 180. Функция полезна для присвоения случайных целых чисел свойствам вроде translateY и rotate. Использование этой функции для анимации масштабирования приведёт к формированию чрезмерно больших или малых масштабов.

Значение степени округления углов для разных элементов определяется путём вычисления ширины целевых элементов с помощью параметра el внутри функции. Последние строки кода выше задают случайные значения параметрам duration и delay.

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

See the Pen Setting Function Based Property Values in Anime.js by Monty (@Shokeen) on CodePen.

Можно анимировать различные свойства целевых объектов с помощью ключевых кадров. Каждый ключевой кадр состоит из объекта массива свойств. Можно использовать этот объект для определения значения свойств, а также для duration, delay и easing. Код ниже создаёт анимацию перехода на основе ключевых кадров:

var keyframeTranslation = anime({
  targets: '.square',
  translateY: [
    { value: 100, duration: 500},
    { value: 300, duration: 1000, delay: 1000},
    { value: 40, duration: 500, delay: 1000}
  ],
  autoplay: false
});
 
var keyframeAll = anime({
  targets: '.square',
  translateY: [
    { value: 100, duration: 500},
    { value: 300, duration: 1000, delay: 1000},
    { value: 40, duration: 500, delay: 1000}
  ],
  scale: [
    { value: 1.1, duration: 500},
    { value: 0.5, duration: 1000, delay: 1000},
    { value: 1, duration: 500, delay: 1000}
  ],
  rotate: [
    { value: 60, duration: 500},
    { value: -60, duration: 1000, delay: 1000},
    { value: 75, duration: 500, delay: 1000}
  ],
  borderRadius: [
    { value: 10, duration: 500},
    { value: 50, duration: 1000, delay: 1000},
    { value: 25, duration: 500, delay: 1000}
  ],
  delay: function(el, i) { return 100 * (i + 1) },
  autoplay: false
});

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

See the Pen Setting Keyframe Based Property Values in Anime.js by Monty (@Shokeen) on CodePen.

Создание и управление временной шкалой анимации

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

Чтобы решить эту проблему, лучше всего использовать временную шкалу для контроля последовательности анимации. Для её создания используется функция anime.timeline(). Также можно передать разные параметры в эту функцию в виде объектов. Эти параметры могут устанавливать направление, в котором временная шкала воспроизводится, количество циклов, а также параметр autoplay, чтобы определить, должна ли анимация воспроизводиться автоматически. С этими параметрами вы можете подробнее ознакомиться в предыдущей части.

На временную шкалу можно добавлять разные анимации с помощью метода add(). Все анимации, добавленные на временную шкалу, будут воспроизведены в том порядке, в котором они были добавлены. Можно указывать абсолютные или относительные значения смещения для управления порядком воспроизведения анимаций.

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

Относительное смещение бывает трёх типов:

+=n — анимация воспроизводится через N миллисекунд после окончания предыдущей.
-=n — анимация воспроизводится за N миллисекунд до окончания предыдущей.
*=n — анимация воспроизводится через то количество миллисекунд, которое равно n-кратной длительности предыдущей анимации. Пример: вы установили смещение, равное 2 ( offset: '*=2' ), предыдущая анимация длится 500 миллисекунд. Умножаем 2 на 500 и получаем 1000 — через этот промежуток времени запустится следующая анимация.

Код ниже демонстрирует, как можно добавить обычную временную шкалу и временную шкалу с относительным смещением:

var basicTimeline = anime.timeline({
  direction: "alternate",
  loop: 2,
  autoplay: false
});
 
basicTimeline.add({
    targets: '.square',
    translateY: 200
 }).add({
    targets: '.red',
    translateY: 100
 }).add({
    targets: '.blue',
    translateY: 0
 });
 
var offsetTimeline = anime.timeline({
  direction: "alternate",
  loop: 2,
  autoplay: false
});
 
offsetTimeline.add({
    targets: '.square',
    translateY: 200
 }).add({
    targets: '.red',
    offset: '+=1000',
    translateY: 100
 }).add({
    targets: '.blue',
    offset: '*=2',
    translateY: 0
 });

See the Pen Creating and Manipulating Timelines in Anime.js by Monty (@Shokeen) on CodePen.

В примере анимации выше нажмите на кнопку Offset Timeline. Вы увидите задержку в 2 секунды между концом анимации красных квадратов и началом анимации синих.

Параметр duration не был определён для красных квадратов, поэтому используется значение по умолчанию, равное 1000 миллисекундам или одной секунде. Смещение с умножением (*=n) для голубых квадратов увеличивает это значение в два раза, в результате чего и появляется задержка анимации в две секунды.

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

Настройки воспроизведения

Функция play() позволяет запустить анимацию либо с начальной позиции, либо с момента, на котором она была остановлена. Pause() останавливает анимацию на время вызова, restart() запускает анимацию с начала, независимо от текущего состояния. Функция seek(n) используется для перемотки анимации, где n — число в миллисекундах.

Если анимация завершилась, её нельзя запустить снова с помощью функции play(). Для повторного воспроизведения анимации используется только функция restart().

var slowAnimation = anime({
  targets: '.square',
  translateY: 250,
  borderRadius: 50,
  duration: 4000,
  easing: 'linear',
  autoplay: false
});
 
document.querySelector('.play').onclick = slowAnimation.play;
document.querySelector('.pause').onclick = slowAnimation.pause;
document.querySelector('.restart').onclick = slowAnimation.restart;
 
var seekInput = document.querySelector('.seek');
 
seekInput.oninput = function() {
  slowAnimation.seek(slowAnimation.duration * (seekInput.value / 100));
};

Обратите внимание, что для перемотки анимации используется не просто seekInput.value. Так было сделано из-за того, что в разметке предопределено максимальное значение для перемотки, равное 100 миллисекундам. Непосредственное использование seekInput.value позволяет осуществлять перемотку только до 100-ой миллисекунды. Путём умножения входного диапазона и длительности анимации можно искать нужный момент на ползунке от начала до конца.

See the Pen Playback Options in Anime.js by Monty (@Shokeen) on CodePen.

Заключение

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

Перевод статьи «JavaScript-Based Animations Using Anime.js, Part 3: Values, Timeline, and Playback»