Существуют тысячи способов сделать анимацию в Интернете. Ранее мы уже рассматривали сравнение различных технологий анимаций. Сегодня же, мы собираемся погрузиться в пошаговое руководство по одному из самых моих любимых способов делать анимации - использование GreenSock.
(Они не платят мне и не подкупают каким-либо еще способом. Мне действительно нравиться их использовать.)
Почему я предпочитаю GreenSock другим методам? Да потому, что зачастую это лучший инструмент для работы. Его использование чрезвычайно просто, даже для сложных анимаций. А вот еще несколько причин, почему я предпочитаю его использовать:
- Вы можете использовать его как для элементов DOM, так и для WebGl/Canvas/Three.js содержимого.
- Замедление на Css создать достаточно сложно. Css-анимации ограничены двумя точками Безье, а это означает, что если вы хотите сделать, скажем эффект отскока, то вам придется создавать ключевые кадры верхнего и нижнего положения для каждого движения. GreenSock позволяет использовать несколько точек Безье для создания более продвинутых эффектов. Отскок - это одна строка кода. Вы можете сами в этом убедиться взглянув на этот визуализатор.
- Вы можете задавать последовательность анимации относительно временной шкалы. Код для css-анимаций становится слегка спагеттевидным, когда вы объединяете несколько анимаций в одно скоординированное движение. GreenSock в такой ситуации остается довольно простым и при этом позволяет контролировать саму шкалу времени.
- GreenSock будет выполнять некоторые вычисления под капотом, чтобы избежать разного поведения в разных браузерах.
- Также GreenSock предлагает расширение функционала в виде плагинов, которые вы можете использовать при необходимости. Например при морфинге SVG форм, рисовании SVG путей, перетаскивании, бросании, анимации с учетом инерции и др.
Люди иногда спрашивают меня, почему я использую именно эту библиотеку, несмотря на множество других вариантов. Потому что она существует дольше чем большинство альтернативных библиотек. Это демо очень вдохновляет, а так-же демонстрирует, что серьезный веб-аниматор может сделать благодаря этому инструменту:
Далее рассмотрим пошаговое описание того, как создавать движения в вебе, до самых маленьких кусочков, которые можно сделать. Итак, приступим!
Анимация DOM элемента
Для примера возьмем шар, который мы создадим с помощью div которому зададим border-radius: 50%. Вот так мы будем масштабировать и перемещать его с помощью GreenSock:
<div class="ball"></div>
gsap.to('.ball', {
duration: 1,
x: 200,
scale: 2
})
В данном случае, мы говорим GreenSock (GSAP) взять элемент с классом .ball и переместить его .to() используя несколько свойств. Мы сократили свойство Css transform: translateX(200px) до короткого x: 200 (обратите внимание, что нет необходимости в единицах измерения, но их можно задавать в виде строки). Мы также не пишем transform: scale(2). Вот трансформации которые вы можете анимировать и соответствующий им синтаксис в CSS.
x: 100 // transform: translateX(100px)
y: 100 // transform: translateY(100px)
z: 100 // transform: translateZ(100px)
// you do not need the null transform hack or hardware acceleration, it comes baked in with
// force3d:true. If you want to unset this, force3d:false
scale: 2 // transform: scale(2)
scaleX: 2 // transform: scaleX(2)
scaleY: 2 // transform: scaleY(2)
scaleZ: 2 // transform: scaleZ(2)
skew: 15 // transform: skew(15deg)
skewX: 15 // transform: skewX(15deg)
skewY: 15 // transform: skewY(15deg)
rotation: 180 // transform: rotate(180deg)
rotationX: 180 // transform: rotateX(180deg)
rotationY: 180 // transform: rotateY(180deg)
rotationZ: 180 // transform: rotateZ(180deg)
perspective: 1000 // transform: perspective(1000px)
transformOrigin: '50% 50%' // transform-origin: 50% 50%
Продолжительность (duration) - это как вы догадались отрезок времени в одну секунду.
Итак, как бы нам оживить, скажем, SVG? Давайте попробуем применить наш код, указанный выше, для SVG.
<svg viewBox="0 0 500 400">
<circle class="ball" cx="80" cy="80" r="80" />
</svg>
gsap.to('.ball', {
duration: 1,
x: 200,
scale: 2
})
С точки зрения анимации, все работает точно также. Находится элемент с классом .ball и к нему применяются указанные свойства. Поскольку SVG - это DOM элементы, мы можем задать класс любому из них и анимировать так же как и любой другой DOM элемент.
Прекрасно!
Eases
Ранее я упоминала, что eases - одна из моих любимых функций и я хочу показать как она работает.
Давайте возьмем наш мяч. Может быть вы хотите попробовать один из уникальных эффектов отскока? Это выглядело бы вот так:
gsap.to('.ball', {
duration: 1.5,
x: 200,
scale: 2,
ease: 'bounce'
})
Вот это да!
В текущей версии GreenSock по умолчанию применяется режим ease-out (что хорошо для начала анимации). Все, что вам нужно сделать - это указать значение "bounce" в виде строки.
Задержки и продолжительность
Я упомянула, что значение по умолчанию ease-out хорошо для начала анимации. А что насчет easy-in или ease-in-out для окончания анимации? Давайте попробуем это сделать.
gsap.to('.ball', {
duration: 1.5,
x: 200,
scale: 2,
ease: 'bounce'
})
gsap.to('.ball', {
duration: 1.5,
delay: 1.5,
x: 0,
scale: 1,
ease: 'back.inOut(3)'
})
Вы могли заметить, что произошло несколько вещей. Например, мы не использовали bounce.in в предпоследней строке (ease: 'back.inOut(3)'). Вместо этого мы использовали другую функцию ease вызвав back для ease-in-out. Мы также передали опцию конфигурации, потому что, как вы можете видеть с помощью визуализатора GreenSock, мы не ограничены только дефолтной конфигурацией для этой функции. Мы можем регулировать ее так, как нам нужно.
Замечательно!
Возможно, вы также заметили, что мы создали анимацию с задержкой. Мы взяли длину первой анимации и сделали для следующей анимации соответствующую задержку. Это вполне неплохо работает, но сложно в поддержке. Что если нам понадобиться изменить продолжительность первой части анимации? Это означает, что мы должны будем изменять и задержку для последующей анимации. А если у нас будет еще одна следующая анимация? И еще одна? Нужно будет рассчитывать и изменять все остальные задержки отталкиваясь от новых данных. Это очень много работы которую прийдется делать вручную.
Мы можем переложить эту работу на компьютер. Некоторые из моих наиболее сложных анимаций - это сотни связанных более простых анимаций! Если я в конце своей работы захочу изменить какие-то данные в самом начале, то я не хочу переделывать всю анимацию.
gsap
.timeline()
.to(‘.ball’, {
duration: 1.5,
x: 200,
scale: 2,
ease: "bounce"
})
.to(‘.ball’, {
duration: 1.5,
x: 0,
scale: 1,
ease: "back.inOut(3)"
});
Этот пример кода создает временную шкалу, а затем связывает между собой две анимации.
Но у нас все еще есть немного дублирования кода, в котором мы продолжаем использовать одну и ту же продолжительность в каждой анимации. Давайте создадим для этого значение по умолчанию, которое будет передано на временную шкалу.
gsap
.timeline({
defaults: {
duration: 1.5
}
})
.to('.ball', {
x: 200,
scale: 2,
ease: "bounce"
})
.to('.ball', {
x: 0,
scale: 1,
ease: "back.inOut(3)"
});
Это очень круто! Ок, вы, вероятно, начинаете понемногу понимать, как все это устроено. Несмотря на то, что наш код пока что не сложен, поверьте, значения по умолчанию и временные шкалы в сложных анимациях помогут сделать ваш код легко поддерживаемым.
Теперь представьте, что нам надо отразить это движение в другом направлении с мячом и просто ... продолжать движение? Другими словами, что если нам надо зациклить его? В таком случае нам надо добавить repeat: -1, и это применимо как к одной анимации, так и ко всей временной шкале.
gsap
.timeline({
repeat: -1,
defaults: {
duration: 1.5
}
})
.to('.ball', {
x: 200,
scale: 2,
ease: "bounce"
})
.to('.ball', {
x: 0,
scale: 1,
ease: "back.inOut(3)"
})
.to('.ball', {
x: -200,
scale: 2,
ease: "bounce"
})
.to('.ball', {
x: 0,
scale: 1,
ease: "back.inOut(3)"
});
Мы можем не просто заставить код повторяться, а заставить его работать в противоположную сторону, как йо-йо. Мы его именно так и назвали - правда. Чтобы показать сам принцип, мы сделаем это только с первой анимацией. Вы можете видеть, что анимация шара воспроизводится вначале вперед, а потом в другую сторону.
gsap
.timeline({
repeat: -1,
yoyo: true,
defaults: {
duration: 1.5
}
})
.to('.ball', {
x: 200,
scale: 2,
ease: "bounce"
})
Нахлёст анимаций и метки
То, что мы можем с легкостью объединять движения в цепочку конечно здорово, но реальные движения работают не совсем так. Если вы идете по комнате, чтобы взять чашку воды, то вы идете не все время. Когда вы дошли, то остановитесь. Потом возьмете чашку и поднимите ее. Потом выпьете воду. Человек делает много разных вещей в одном непрерывном движении. Итак, давайте вкратце поговорим о том, как прекращать движение анимации и сразу же начинать следующее.
Если мы хотим быть уверены, что все происходит немного раньше или позже друг друга на временной шкале, мы можем использовать инкремент или декремент. На следующем примере три шара анимируются один за другим и это выглядит грубовато.
gsap
.timeline({
defaults: {
duration: 1.5
}
})
.to('.ball', {
x: 300,
scale: 2,
ease: "bounce"
})
.to('.ball2', {
x: 300,
scale: 2,
ease: "bounce"
})
.to('.ball3', {
x: 300,
scale: 2,
ease: "bounce"
})
Анимация становится более гладкой, если мы слегка перекрываем движение, используя эти декременты, переданные в виде строк:
gsap
.timeline({
defaults: {
duration: 1.5
}
})
.to('.ball', {
x: 300,
scale: 2,
ease: "bounce"
})
.to('.ball2', {
x: 300,
scale: 2,
ease: "bounce"
}, '-=1')
.to('.ball3', {
x: 300,
scale: 2,
ease: "bounce"
}, '-=1')
Еще один способ сделать это - использовать метку. Метки гарантируют, что все начинается в определенный момент времени в анимации воспроизведения. Это выглядит так: .add ('labelName').
gsap
.timeline({
defaults: {
duration: 1.5
}
})
.add('start')
.to('.ball', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start')
.to('.ball2', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start')
.to('.ball3', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start')
Мы можем даже добавлять или уменьшать время начала анимации отталкиваясь от метки. Это выглядит так: 'start+=0.25'.
gsap
.timeline({
defaults: {
duration: 1.5
}
})
.add('start')
.to('.ball', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start')
.to('.ball2', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start+=0.25')
.to('.ball3', {
x: 300,
scale: 2,
ease: "bounce"
}, 'start+=0.5')
Уф! Мы можем так много сделать с этим! Вот пример анимации, которая объединяет многие из примеров выше с небольшим взаимодействием с использованием ванильного JavaScript. Не забудьте нажать на звонок.
Если вы ищете больше информации про анимацию с помощью GreenSock, вот статья которую я написала о том как это сделать во Vue, и мое выступление посвященное React (ему пару лет, но базовые вещи все еще применимы).
В этой статье мы не рассмотрели очень много вещей: изменение и рисование SVG, разбрасывание объектов по экрану, перемещение объектов по заданной траектории, анимирование текстов… Я бы посоветовала вам обратиться к документации GreenSock за этими подробностями. У меня также есть курс Frontend Masters в котором мы рассматриваем это все гораздо подробнее и глубже, а также есть материалы с открытым исходным кодом на моем GitHub. У меня таже есть много Pens с кодом которых можно смело экспериментировать.
Надеюсь это поможет начать вам работу с анимацией в Интернете! Я уже не могу дождаться, чтобы увидеть, что вы сделаете!