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

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

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

Callback-функции

Обратные вызовы используются для выполнения функций, основанных на прогрессе анимации. В Anime.js существует 4 функции обратного вызова: begin, run, update и comlete. Каждая из них запускается в определённое время и принимает объект анимации в качестве своего аргумента.

Функция begin() вызывается, когда анимация начинается. Это значит, что если у анимации есть параметр delay со значением 800 миллисекунд, то begin() будет вызвана только через 800 миллисекунд. Можно проверить, запустилась анимация или нет, используя функцию animationName.begin, которая возвращает true (запустилась) или false (не запустилась).

Run используется для выполнения функции в каждом кадре после запуска анимации. Если нужно выполнить функцию в каждом кадре с самого начала анимации, независимо от параметра delay, то используйте callback-функцию update.

Callback-функция complete похожа на begin, только вызывается она после окончания. Чтобы проверить, завершилась анимация или нет, используйте animationName.complete, как и в случае с begin.

Callback-функцию update мы использовали ещё в первой части для обновления количества сканированных и заражённых файлов. В этой статье мы дополним пример со сканированием, и вы увидите, как работают все callback-функции.

var filesScanned = { count: 0, infected: 0 };
var scanCount = document.querySelector(".scan-count");
var infected = document.querySelector(".infected-count");
 
var scanning = anime({
  targets: filesScanned,
  autoplay: false,
  count: 100,
  infected: 8,
  delay: 1000,
  duration: 2000,
  easing: "linear",
  round: 1,
  update: function(anim) {
    if (anim.currentTime < 1000) {
      document.querySelector(".update-cb").innerHTML = "Creating an Index...";
    }
  },
  begin: function() {
    document.querySelector(".begin-cb").innerHTML = "Starting the Scan...";
  },
  run: function() {
    scanCount.innerHTML = filesScanned.count;
    infected.innerHTML = filesScanned.infected;
  },
  complete: function() {
    document.querySelector(".complete-cb").innerHTML = "Scan Complete...";
  }
});

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

Сама анимация начинается с задержкой в 1000 миллисекунд, и именно в этот момент срабатывает функция begin, которая показывает пользователю сообщение «Starting the Scan…». В то же время run начинает выполняться и обновлять числовые значения объекта после каждого кадра. После окончания анимации функция обратного вызова complete отображает сообщение «Scan Complete…».

See the Pen Using Callbacks in Anime.js by Monty (@Shokeen) on CodePen.

Функции плавности

Функции плавности используются для контроля перехода начального значения свойства в конечное. Эти функции можно определить с помощью параметра easing, который может принимать значения как в виде строк, так и в виде пользовательских координат кривых Безье (в виде массива).

Существует 31 встроенная функция плавности. Одна из них называется linear, остальные 30 состоят из разных вариаций easeIn, easeOut и easeInOut. Класc elastic определяет три функции плавности: easeInElastic, easeOutElastic и easeInOutElastic. Вы можете управлять ими с помощью параметра elasticity. Значение этого параметра может находиться только в диапазоне от 0 до 1000.

Использование easeIn ускоряет изменение значения, начиная с нуля. Это значит, что изменяться оно будет сначала медленно, а в конце — быстро. Скорость изменения в начале равна нулю, а в конце — 1000.

Функция easeOut замедляет изменение значения, начиная с максимальной скорости.

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

See the Pen Comparison of Different Easing Functions in Anime.js by Monty (@Shokeen) on CodePen.

С помощью anime.easings можно создавать собственные функции плавности. Ниже приведён пример создания пользовательских функций плавности:

anime.easings['tanCube'] = function(t) {
  return Math.pow(Math.tan(t * Math.PI / 4), 3);
}
 
anime.easings['tanSqr'] = function(t) {
  return Math.pow(Math.tan(t * Math.PI / 4), 2);
}
 
var tanCubeSequence = anime({
  targets: '.tan-cube',
  translateX: '75vw',
  duration: 2000,
  easing: 'tanCube',
  autoplay: false
});
 
var tanSqrSequence = anime({
  targets: '.tan-sqr',
  translateX: '75vw',
  duration: 2000,
  easing: 'tanSqr',
  autoplay: false
});

See the Pen Creating Custom Easing Functions in Anime.js by Monty (@Shokeen) on CodePen.

Анимации на основе SVG-файлов

Во всех связанных с движением анимациях, которые были созданы до этого момента, целевые элементы перемещались по прямой линии. В Anime.js можно перемещать элементы по сложным SVG-контурам с большим количеством кривых с возможностью контроля положения и угла анимируемых элементов на контуре. Чтобы переместить элемент по оси X на контуре, используйте path('x'). Подобным образом элементы можно перемещать по оси Y, используя path('y').

Если контур не представлен в виде прямой линии, то он почти всегда будет формировать угол относительно основной горизонтальной линии. При вращении любого некруглого элемента анимации общая картинка будет выглядеть более естественно, если элемент будет перемещаться по углу контура. Это можно сделать, установив значение свойства rotate равным path('angle'). Ниже представлен пример кода, который анимирует четыре элемента с разными значениями плавности по SVG-контуру:

var path = anime.path('path');
 
var easings = ['linear', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic'];
 
var motionPath = anime({
  targets: '.square',
  translateX: path('x'),
  translateY: path('y'),
  rotate: path('angle'),
  easing: function (el, i) {
    return easings[i];
  },
  duration: 10000,
  loop: true
});

Во вставке ниже видно, что красный квадрат с функцией плавности easeInCubic двигается медленнее всех в начале, но быстрее всех в конце. Похожая ситуация и в случае с оранжевым квадратом — быстрее всего он двигается в начале, но медленнее всех в конце.

See the Pen Motion Along an SVG Path in Anime.js by Monty (@Shokeen) on CodePen.

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

var morphing = anime({
  targets: 'polygon',
  points: [
    { value: '143 31 21 196 286 223' },
    { value: '243 31 21 196 286 223' },
    { value: '243 31 121 196 286 223' },
    { value: '243 31 121 196 386 223' },
    { value: '543 31 121 196 386 223' }
  ],
  easing: 'linear',
  duration: 4000,
  direction: 'alternate',
  loop: true
});

See the Pen Morphing SVG Shapes in Anime.js by Monty (@Shokeen) on CodePen.

Одним из наиболее интересных эффектов Anime.js является возможность создания линейных рисунков. Всё, что нужно сделать — предоставить библиотеке контур, который вы хотите использовать для линейного рисунка; предоставить другие параметры, с помощью которых контролируется продолжительность, задержка и плавность. В примере ниже использовалась функция обратного вызова complete, чтобы сделать заливку рисунка якоря из Font Awesome жёлтым цветом.

var lineDrawing = anime({
  targets: 'path',
  strokeDashoffset: [anime.setDashoffset, 0],
  easing: 'easeInOutCubic',
  duration: 4000,
  complete: function(anim) {
    document.querySelector('path').setAttribute("fill", "yellow");
  }
});

See the Pen SVG Line Drawings in Anime.js by Monty (@Shokeen) on CodePen.

Используя знания всех изученных понятий, вы сможете создавать более сложные линейные изображения с гораздо лучшим контролем над тем, как они рисуются. Ниже приводится пример отрисовки имени с помощью SVG:

var letterTime = 2000;
 
var lineDrawing = anime({
  targets: "path",
  strokeDashoffset: [anime.setDashoffset, 0],
  easing: "easeInOutCubic",
  duration: letterTime,
  delay: function(el, i) {
    return letterTime * i;
  },
  begin: function(anim) {
    var letters = document.querySelectorAll("path"), i;
 
    for (i = 0; i < letters.length; ++i) {
      letters[i].setAttribute("stroke", "black");
      letters[i].setAttribute("fill", "none");
    }
  },
  update: function(anim) {
    if (anim.currentTime >= letterTime) {
      document.querySelector(".letter-m").setAttribute("fill", "#e91e63");
    }
    if (anim.currentTime >= 2 * letterTime) {
      document.querySelector(".letter-o").setAttribute("fill", "#3F51B5");
    }
    if (anim.currentTime >= 3 * letterTime) {
      document.querySelector(".letter-n").setAttribute("fill", "#8BC34A");
    }
    if (anim.currentTime >= 4 * letterTime) {
      document.querySelector(".letter-t").setAttribute("fill", "#FF5722");
    }
    if (anim.currentTime >= 5 * letterTime) {
      document.querySelector(".letter-y").setAttribute("fill", "#795548");
    }
  },
  autoplay: false
});

В примере выше для переменной letterTime установлено значение 2 000 миллисекунд. Это то время, которое будет затрачено на рисование одной буквы имени. Свойство delay использует функции, основанные на индексе параметра, чтобы установить соответствующее значение delay с помощью переменной letterTime.

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

Внутри функции обратного вызова begin было установлено значение black для обводки букв (stroke), а для fill — none. Таким образом можно очистить все цветовые значения, применённые внутри callback-функции, и они вернутся в своё исходное положение при запуске нескольких циклов. Нажмите на кнопку «Write the Name» в примере ниже и увидите код в действии.

See the Pen SVG Line Drawings in Anime.js – II by Monty (@Shokeen) on CodePen.

Заключение

Из этой статьи вы узнали о разных функциях обратного вызова, которые используются для выполнения задач вроде обновления DOM или изменения значения атрибута, основанного на прогрессе анимации. Также вы познакомились с разными функциями плавности и узнали, как их создают. Кроме того, в последней секции были представлены примеры использования SVG-файлов в Anime.js.

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

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

Перевод статьи «JavaScript-Based Animations Using Anime.js, Part 4: Callbacks, Easings, and SVG»