Статьи

Анимации на основе JavaScript с использованием Anime.js, часть 4: обратные вызовы, упрощения и SVG

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

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

Как я упоминал во введении, вы можете использовать обратные вызовы для выполнения функций, основанных на ходе анимации. Существует четыре различных обратных вызова: begin , run , update и complete . Каждая функция обратного вызова запускается в определенное время, и каждая принимает объект анимации в качестве аргумента.

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

Обратный вызов run можно использовать для выполнения функции в каждом кадре после начала воспроизведения анимации. Если вы хотите выполнить функцию в каждом кадре с самого начала анимации, независимо от ее delay , вы должны использовать вместо этого обратный вызов update .

complete обратный вызов аналогичен begin за исключением того, что он вызывается после завершения воспроизведения анимации. Как и в begin , вы можете использовать animationName.complete чтобы проверить, закончилась ли анимация или нет.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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…»;
  }
});

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

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

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

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

Уравнения EaseIn ускоряют изменение значения свойства, начиная с нуля. Это означает, что изменение значения будет медленным в начале и очень быстрым в конце. Скорость изменения равна нулю в начале и максимальна в конце. Уравнения EaseOut замедляют изменение значения свойства, начиная с изменения максимальной скорости.

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
});

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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 самый медленный в начале и самый быстрый в конце. Точно так же оранжевый квадрат с easeOutCubic является самым быстрым в начале и самым медленным в конце.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
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
});

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

1
2
3
4
5
6
7
8
9
var lineDrawing = anime({
  targets: ‘path’,
  strokeDashoffset: [anime.setDashoffset, 0],
  easing: ‘easeInOutCubic’,
  duration: 4000,
  complete: function(anim) {
    document.querySelector(‘path’).setAttribute(«fill», «yellow»);
  }
});

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
});

Я начинаю с присвоения значения 2000 переменной letterTime . Это время, которое я хочу, чтобы Anime.js занимал, пока он рисует каждую букву моего имени. Свойство delay использует параметр индекса на основе функции для установки соответствующего значения delay с помощью переменной letterTime .

Индекс первой буквы «M» равен нулю, поэтому Anime.js начинает рисовать его немедленно. Буква «О» имеет задержку 2000 мс, потому что это время, необходимое для полного рисования буквы «М».

В обратном вызове begin я установил значение stroke всех букв black а их значения fill равными none . Таким образом, мы можем очистить все значения цвета, примененные в обратном вызове update чтобы буквы могли вернуться в исходное состояние при запуске в несколько циклов. Попробуйте нажать кнопку « Написать имя» в следующей демонстрации, чтобы увидеть код в действии.

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

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