Сайт вебмастера

Библиотека для анимации Anime.js

01-02-2021Время чтения ~ 7 мин.Alpine.js, jQuery и JavaScript 4368

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

У такого подхода есть несколько ограничений. Поскольку сама анимация жестко прописывается в виде класса, то для изменений требуется новая sass-компиляция или новый css-класс. Если, скажем, мы описываем анимацию цвета, то для 10 вариантов потребуется как минимум 10 новых классов. Аналогичная ситуация с размерами, отступами и т.д. То есть анимация в виде css-классов больше рассчитана на какие-то «фиксированные»/«типовые» эффекты.

Я уже много писал про Alpine.js как замену jQuery. Но у jQuery есть специальный метод .animate(), с помощью которого можно создать анимацию. Она довольно ограниченная, но для многих задач её хватает. В Alpine.js такого нет, поэтому я нашёл отдельную библиотеку Anime.js, с помощью которой можно создавать совершенно потрясающие вещи.

Anime.js прекрасно дополняет Альпину. Примеры я будут показывать именно в такой связке, поскольку это не только здорово упрощает js-код, но и позволяет задействовать lazy-загрузку js-файлов в конце BODY.

К достоинствам Anime.js можно отнести и очень малый размер js-файла — всего 17Кб (это примерно 7Кб трафика).

Рассмотрим первый простой пример - анимация кнопки:

<button x-data @click="anime({
		targets: $el,
		duration: 1000,
		easing: 'linear',
		rotate: [360, 0]
	});" class="button button1">Animate</button>

Это компонент Альпины, где мы цепляемся к клику по кнопке. Функция anime() принадлежит Anime.js — собственно именно она и выполняет анимацию. Магическая переменная Альпины $el указывает на текущий DOM-элемент. То есть этот код можно трактовать как «сделай анимацию текущего элемента по клику».

Функция anime() принимает ряд параметров.

  • targets — это элемент, который будет анимироваться
  • duration — время анимации в милисекундах
  • easing — «временная» функция анимации, здесь анимация линейная
  • rotate — это css-свойство, которое будет анимироваться, в данном случае это transform: rotate()

В течение 1 секунды кнопка вначале развернётся на 360 градусов, а потом вернётся к нулевому положению. То есть значения css-свойства можно записывать как в виде одного числа (это будет одно действие), так и в виде массива.

<button x-data @click="anime({
		targets: $el,
		duration: 1000,
		easing: 'easeInOutQuad',
		rotate: '+=720'
	});" class="button button1">Animate</button>

В этом примере анимация устанавливает свойство rotate в 720 градусов к предыдущему значению.

Если указать просто 720, то повторное нажатие кнопки уже не изменит это значение, поскольку оно уже установлено. Но если мы укажем [720, 0], то анимация вернётся в начальную точку. Либо использовать функцию сброса анимации перед её началом.

Рассмотрим более сложный пример:

<div x-data @click="anime({
		targets: $el,
		duration: 3000,
		rotate: [360, 60, 30, 15, 0, -3, -7, -30, 0],
		easing: 'linear',
		complete: function () {
		   $el.classList.toggle('bg-green500');
		   $el.classList.toggle('bg-blue500');
		},
	});" class="w200px h200px bg-green500"></div>

Здесь вращение, которое задаётся в виде массива значений, а также функция complete(), которая срабатывает после завершении анимации. Заметьте, что в начале цвет квадрата зелёный, а потом меняется на синий.

Anime.js позволяет анимировать любое css-свойство (в отличие от jQuery). Вот такой пример:

<div x-data @click="play2($el)" class="w200px h200px bg-red500"></div>

<script>
function play2(el) {
    let an = anime.timeline({
      targets: el,
      duration: 700,
      easing: 'linear',
    });

    an
      .add({
        backgroundColor: '#FFFF00',
        scale: 1.1,
        borderRadius: ['0%', '50%'],
      })
      .add({
        backgroundColor: '#00FF00',
        scale: .9,
      })
      .add({
        backgroundColor: '#00FFFF',
        scale: 1.3,
      })
      .add({
        backgroundColor: '#0000FF',
        scale: 1,
        opacity: .5,
      })
      .add({
        backgroundColor: '#FF0000',
        scale: 1.2,
      })
      .add({
        scale: 1,
        borderRadius: ['50%', '0%'],
      })      
}
</script>

Функцию анимации я вынес отдельно, чтобы не загромождать HTML. Функция timeline() задаёт базовую настройку анимации. А функции add() задают timeline — то есть вначале будет выполнена первая анимация, потом вторая и т.д.

Во всех примерах анимация применяется к текущему элементу. Но, что делать, если анимировать нужно сразу несколько? Здесь опять же на помощь приходит «магия» Alpine.js: используем $refs.

Прямая - это убежавшая точка

<div x-data @click="
	anime({
		targets: $refs.ht,
		duration: 700,
		easing: 'linear',
		translateY: [20, 0 -10, 0],
		translateX: [0, 15],
	});
	
	anime({
		targets: $refs.bt,
		duration: 500,
		delay: 300,
		easing: 'linear',
		translateY: [0, -7],
		width: [0, '100%'],
		height: [0, 14],
		backgroundColor: ['#fff', '#FACC60'],
		borderRadius: [0, 5],
	});">
	
	<h1 x-ref="ht" class="mar0 pos-relative z-index1 t-red t-impact">Прямая - это убежавшая точка</h1>
	<div x-ref="bt"></div>
</div>

Здесь запускается анимация для $refs.ht и $refs.bt с той разницей, что для второго элемента дополнительно прописана задержка в параметре delay. Но идея в том, что анимировать можно любые элементы по любым js-событиям.

Попробуем теперь повторить некоторые jQuery-эффекты. Самый простой это fadeOut — исчезновение через прозрачность.

Текст текст fadeOut
<div x-data @click="anime({
	targets: $el,
	duration: 1000,
	opacity: 0,
	easing: 'linear',
});" class="t200 bg-green">Текст текст fadeOut</div>

Теперь эффект slideUp — когда элемент смещается вверх. Здесь нам нужно уже задать дополнительный контейнер, который скрывает выходящее за его границы содержимое. Сама анимация применяется к css-свойству top — мы смещаем элемент вверх (отрицательное число) на всю его высоту.

Текст текст slideUp
<div class="overflow-hidden">
	<div x-data @click="anime({
		targets: $el,
		duration: 1000,
		top: -$el.clientHeight,
		easing: 'linear',
	});" class="t200 bg-green pos-relative">Текст текст slideUp</div>
</div>

Аналогично можно сделать slideDown, где тот же алгоритм, только top увеличивается.

Текст текст slideDown
<div class="overflow-hidden">
	<div x-data @click="anime({
		targets: $el,
		duration: 1000,
		top: $el.clientHeight,
		easing: 'linear',
	});" class="t200 bg-green pos-relative">Текст текст slideDown</div>
</div>

В этих примерах после анимации блок всё равно занимает исходное место. Чтобы его спрятать можно ещё анимировать свойство height и opacity для красоты.

Текст текст slideUp + fadeOut + height
<div class="overflow-hidden">
	<div x-data @click="anime({
		targets: $el,
		duration: 1000,
		opacity: 0,
		top: -$el.clientHeight,
		height: 0,
		easing: 'linear'
	});" class="t200 bg-green pos-relative">Текст текст slideUp + fadeOut + height</div>
</div>

Либо можно высоту не трогать, а потом её уменьшить до нуля в complete:

Текст текст slideUp + fadeOut + height
<div class="overflow-hidden">
	<div x-data @click="anime({
		targets: $el,
		duration: 1000,
		opacity: 0,
		top: -$el.clientHeight,
		easing: 'linear',
		complete: function () {
			$el.style.height = 0;
		},
	});" class="t200 bg-green pos-relative">Текст текст slideUp + fadeOut + height</div>

Минус такого подхода в том, что возникает «скачок» отображения нижнего блока. Хотя, можно добавить ещё одну анимацию после исчезновения блока, анимировать его высоту до нуля.

Итого

Anime.js я стал использовать не так давно, когда стала задача анимировать один из компонентов шапки в MF. Я долго боролся с этой задачкой через привычные css-классы, но там были сложные условия (несколько положений по onscroll), поэтому решил попробовать Anime.js и был приятно удивлён. Анимацией я никогда не занимался, но с Anime.js это оказалось очень просто.

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

Для меня Anime.js цена ещё и тем, что она фактически закрывает проблему перехода с jQuery на Alpine.js для тех случаев, когда нужна анимация. Даже если сравнивать размеры, то jQuery 3.5 — 89Кб и это примерно в 2 раза больше, чем сумма Alpine + Anime (25Кб и 17Кб). С учётом их потрясающих возможностей можно смело выкидывать jQuery на «свалку истории». :-)

Похожие записи