Среди JavaScript-библиотек для анимации Anime.js занимает особое место: она весит меньше 17 КБ, покрывает практически любой сценарий — от простого перемещения блока до синхронизированных таймлайнов — и при этом читается как обычный JavaScript, а не как заклинание на непонятном языке. Anime.js доступна на GitHub, поддерживается всеми современными браузерами и активно развивается.
В этой статье разберём, как делать JavaScript-анимации на сайте с помощью Anime.js — от подключения до сложных таймлайнов — с живыми примерами прямо на странице. Можно нажимать «Перезапустить» столько раз, сколько захочется.
Подключение
Два способа на выбор. Если проект простой или нужно быстро попробовать — CDN:
<!-- Перед </body> -->
<script src="https://cdn.jsdelivr.net/npm/animejs@3/lib/anime.min.js"></script> Для проектов с npm/Yarn:
npm install animejs
# или
yarn add animejs После установки импортируйте библиотеку в нужном модуле:
// ES-модуль
import anime from 'animejs'; Первая анимация
Вся работа с Anime.js строится вокруг одной функции — anime() . Она принимает объект с описанием анимации и запускает её:
anime({
/* описание анимации */
}); Внутри объекта — четыре группы свойств:
- Цели ( targets ) — что анимируем. CSS-селектор, DOM-элемент, NodeList или JavaScript-объект.
- Свойства — что именно меняется: CSS-свойства, трансформации, атрибуты SVG, числовые поля объектов.
- Параметры свойств — duration , delay , easing и другие — задаются для каждого свойства отдельно или глобально.
- Параметры анимации — direction , loop , autoplay .
Посмотрим на практике. Зелёный квадрат смещается вправо и превращается в круг — затем возвращается обратно:
anime({
targets: '.box',
translateX: 120, // сместить на 120px вправо
borderRadius: '50%', // превратить в круг
duration: 1800,
easing: 'easeInOutSine',
direction: 'alternate', // проиграть в обратном порядке после завершения
loop: true,
}); <div class="box"></div> .box {
width: 60px;
height: 60px;
background-color: hsl(140, 60%, 50%);
border-radius: 4px;
} Обратите внимание — единицы измерения писать не нужно, Anime.js добавляет их автоматически. Если нужно что-то нестандартное — "120rem" или "50%" — просто оберните значение в строку.
Функции плавности в Anime.js
Easing — это кривая скорости анимации: с каким ускорением объект начинает движение и замедляется в конце. Anime.js включает полный арсенал встроенных функций:
- Линейные: linear
- Стандартные кривые: easeInSine , easeOutSine , easeInOutSine — и аналоги для Quad, Cubic, Quart, Quint, Expo, Circ, Back, Elastic, Bounce
- Пружинная: easeOutElastic(1, .5) — с настраиваемой амплитудой и периодом
- Шаги: steps(5) — дискретная анимация, как в ретро-играх
- Кубическая кривая Безье: cubicBezier(.5, .05, .1, .3)
Для имитации физически достоверных движений чаще всего используют easeInOutSine и easeOutElastic. Для UI-переходов кнопок и карточек — easeOutQuad. linear подходит там, где нет резких остановок: бесконечные вращения, progress-бары.
Пример: анимация маятника
Маятник — хороший пример физически достоверной анимации. Ключевой приём здесь — задать диапазон значений вместо одного числа и использовать easeInOutSine , который точно воспроизводит замедление на пиках и ускорение внизу:
anime({
targets: '#rod',
rotate: [50, -50], // от +50° до -50°
duration: 2000,
easing: 'easeInOutSine',
direction: 'alternate',
loop: true,
}); <div class="pendulum-wrap">
<div class="pendulum-pivot"></div>
<div id="rod">
<div class="pendulum-ball"></div>
</div>
</div> .pendulum-wrap {
display: flex;
justify-content: center;
}
.pendulum-pivot {
width: 12px;
height: 12px;
background-color: hsl(210, 20%, 60%);
border-radius: 50%;
position: absolute;
}
#rod {
width: 4px;
height: 120px;
background-color: hsl(210, 20%, 60%);
transform-origin: top center;
}
.pendulum-ball {
width: 32px;
height: 32px;
background-color: hsl(40, 90%, 60%);
border-radius: 50%;
margin: 0 auto;
transform: translateX(-14px);
} Параметр direction: alternate заставляет анимацию воспроизводиться в обратном порядке после завершения — именно это создаёт эффект маятника, а не телеграфного столба.
Stagger — анимация нескольких элементов
Если запустить одну анимацию сразу на десяти карточках — они все дёрнутся одновременно. Технически правильно, визуально — топорно. Stagger решает это: он автоматически добавляет нарастающую задержку каждому элементу, создавая каскадный эффект.
Вместо того чтобы прописывать delay вручную для каждого элемента, достаточно одной строки:
// Каждый следующий элемент стартует на 150 мс позже предыдущего
delay: anime.stagger(150)
// Задержка нарастает от центра к краям
delay: anime.stagger(100, { from: 'center' })
// Обратный порядок: последний элемент стартует первым
delay: anime.stagger(200, { direction: 'reverse' }) По умолчанию нулевую задержку получает первый элемент, каждый следующий ждёт чуть дольше. Параметр from меняет, с какого элемента начинается отсчёт: 'center' — волна расходится от середины к краям, 'last' — с конца к началу. Можно передать конкретный индекс — from: 3 — тогда нулевую задержку получит четвёртый элемент, а остальные будут ждать пропорционально расстоянию от него. direction: "reverse" работает иначе: он не меняет точку отсчёта, а просто переворачивает порядок — последний стартует первым.
anime({
targets: '.wave-bar',
scaleY: [0, 8, 0],
backgroundColor: ['hsl(210, 70%, 60%)', 'hsl(270, 80%, 72%)', 'hsl(210, 70%, 60%)'],
duration: 1400,
delay: anime.stagger(80, { from: 'center' }),
easing: 'easeInOutSine',
loop: true,
}); <div class="wave-wrap">
<div class="wave-bar"></div>
<div class="wave-bar"></div>
<!-- ещё 10 таких же... -->
</div> .wave-wrap {
display: flex;
align-items: center;
gap: 5px;
}
.wave-bar {
width: 10px;
height: 10px;
background-color: hsl(210, 70%, 60%);
border-radius: 5px;
transform: scaleY(0); /* скрыть до старта анимации */
transform-origin: center;
} Пример: анимация зарядки аккумулятора
Три сегмента батареи расширяются один за другим. anime.stagger(500) добавляет 500 мс между стартом каждого сегмента. Вторая анимация считает значение прогресса от 0 до 100 и выводит его в элемент — через callback update , который вызывается на каждом кадре:
const battery = { progress: 0 };
// Анимация сегментов — справа налево через direction: 'reverse'
anime({
targets: '.segment',
scaleX: [0, 1],
duration: 300,
delay: anime.stagger(500, { direction: 'reverse' }),
endDelay: 600,
easing: 'linear',
loop: true,
});
// Счётчик синхронизирован по длительности с анимацией сегментов
const battery = { progress: 0 };
anime({
targets: battery,
progress: [0, 100],
duration: 1300,
endDelay: 600,
easing: 'linear',
round: 1,
loop: true,
update: () => {
document.querySelector('.battery-progress').textContent = battery.progress + '%';
},
}); <div class="battery-wrap">
<div class="battery">
<div class="battery-tip"></div>
<div class="battery-body">
<div class="segment"></div>
<div class="segment"></div>
<div class="segment"></div>
</div>
</div>
<div class="battery-progress">0%</div>
</div> .battery-tip {
width: 8px;
height: 24px;
border-radius: 4px 0 0 4px;
}
.battery-body {
display: flex;
align-items: center;
gap: 4px;
padding: 6px;
border: 3px solid #888;
border-radius: 0 6px 6px 0;
}
.segment {
width: 24px;
height: 32px;
border-radius: 3px;
transform: scaleX(0);
transform-origin: right center; /* раскрывается справа налево */
} Параметр round: 1 округляет анимируемое значение до целых чисел — без него счётчик показывал бы дробные проценты. Оба вызова anime() работают независимо, но с одинаковой суммарной длительностью (1300 + 600 мс), поэтому цифра и сегменты остаются в синхронизации бесконечно.
Кейфреймы — многоступенчатые анимации
Базовая анимация переводит элемент из точки А в точку Б. Кейфреймы позволяют задать произвольный маршрут: А → Б → В → Г. Для каждого свойства передаётся массив объектов, где каждый объект — один шаг с собственными duration и delay :
anime({
targets: '#dot',
translateY: [
{ value: -36, duration: 400 },
{ value: 0, duration: 400, delay: 800 },
{ value: 36, duration: 400, delay: 400 },
{ value: 0, duration: 400, delay: 800 },
],
translateX: [
{ value: 36, duration: 400, delay: 400 },
{ value: 0, duration: 400, delay: 800 },
{ value: -36, duration: 400, delay: 400 },
{ value: 0, duration: 400, delay: 800 },
],
easing: 'easeInOutQuad',
loop: true,
}); <div class="keyframes-wrap">
<div id="dot"></div>
</div> .keyframes-wrap {
display: flex;
align-items: center;
justify-content: center;
height: 120px;
}
#dot {
width: 20px;
height: 20px;
background-color: hsl(200, 80%, 60%);
border-radius: 50%;
} Кейфреймы translateY и translateX выполняются параллельно: оба массива отсчитывают время независимо с самого начала. Именно из комбинации горизонтального и вертикального движений складывается обход по периметру. Это требует аккуратных расчётов duration и delay — но результат стоит усилий.
Текстовые JS-анимации
Anime.js не знает ничего о тексте — но умеет анимировать любой DOM-элемент. Разобьём слово на отдельные <span> и применим stagger для каскадного эффекта. Функция anime.stagger([0.5, 1], { from: 'center' }) задаёт диапазон масштаба — буквы в середине меньше, по краям крупнее:
anime({
targets: '.letter',
opacity: [0, 1],
translateY: [40, 0],
rotate: {
value: 360,
duration: 1600,
easing: 'easeInExpo',
},
scale: anime.stagger([0.5, 1], { from: 'center' }),
delay: anime.stagger(120, { start: 300 }),
easing: 'easeOutExpo',
duration: 1200,
}); <div class="text-wrap">
<span class="letter">A</span>
<span class="letter">n</span>
<span class="letter">i</span>
<span class="letter">m</span>
<span class="letter">e</span>
<span class="letter">.</span>
<span class="letter">j</span>
<span class="letter">s</span>
</div> .text-wrap {
display: flex;
justify-content: center;
font-size: 2.5em;
font-weight: 700;
}
.letter {
display: inline-block;
opacity: 0;
} Обратите внимание: opacity: [0, 1] — это запись «от → до». Аналогичная нотация работает для любого свойства, когда нужно явно задать начальное значение вместо того, чтобы оно читалось из CSS.
Таймлайны в Anime.js
Таймлайн — это инструмент для координации нескольких анимаций во времени. Анимации добавляются методом .add() и по умолчанию выстраиваются последовательно. Второй аргумент .add() задаёт смещение относительно предыдущей анимации: '-=700' означает «начать на 700 мс раньше конца предыдущей»:
const tl = anime.timeline({
duration: 800,
easing: 'easeInOutSine',
loop: true,
});
tl.add({
targets: '.ball-one',
keyframes: [{ translateY: -60 }, { translateY: 0 }],
}).add({
targets: '.ball-two',
keyframes: [{ translateY: -60 }, { translateY: 0 }],
}, '-=700').add({ // старт на 700мс раньше конца предыдущей
targets: '.ball-three',
keyframes: [{ translateY: -60 }, { translateY: 0 }],
}, '-=700'); <div class="balls-wrap">
<div class="ball-one"></div>
<div class="ball-two"></div>
<div class="ball-three"></div>
</div> .balls-wrap {
display: flex;
justify-content: center;
align-items: flex-end;
gap: 24px;
height: 100px;
}
.ball-one,
.ball-two,
.ball-three {
width: 32px;
height: 32px;
border-radius: 50%;
}
.ball-one { background-color: hsl(200, 80%, 60%); }
.ball-two { background-color: hsl(270, 80%, 72%); }
.ball-three { background-color: hsl(340, 80%, 65%); } Глобальные параметры таймлайна — duration , easing , loop — наследуются всеми добавленными анимациями, но могут быть переопределены в каждой по отдельности.
Callbacks и управление JS-анимацией
Anime.js предоставляет набор callback-функций, которые срабатывают в ключевые моменты анимации:
anime({
targets: '.box',
translateX: 200,
begin: (anim) => {
// Вызывается один раз перед первым кадром
console.log('Анимация началась');
},
update: (anim) => {
// Вызывается на каждом кадре
console.log('Прогресс:', anim.progress + '%');
},
complete: (anim) => {
// Вызывается после последнего кадра
console.log('Готово!');
},
loopBegin: (anim) => { /* Начало каждой итерации loop */ },
loopComplete: (anim) => { /* Конец каждой итерации loop */ },
changeBegin: (anim) => { /* Перед изменением значений */ },
changeComplete: (anim) => { /* После изменения значений */ },
}); Методы управления воспроизведением — вызываются на экземпляре анимации:
const anim = anime({ targets: '.box', translateX: 200 });
anim.play(); // запустить / продолжить
anim.pause(); // поставить на паузу
anim.restart(); // сбросить и проиграть с начала
anim.reverse(); // изменить направление
anim.seek(1000); // перейти к отметке 1000мс
anim.seek(anim.duration); // перейти в конечное состояние SVG-анимации
Anime.js отлично работает с SVG. Два наиболее востребованных сценария — анимация по пути и морфинг форм.
Анимация по пути
Метод anime.path() превращает SVG-путь в функцию, которая возвращает координаты и угол поворота в любой точке:
const path = anime.path('.track');
anime({
targets: '.dot',
translateX: path('x'), // смещение по X вдоль пути
translateY: path('y'), // смещение по Y вдоль пути
rotate: path('angle'), // угол касательной — эллипс сам поворачивается
duration: 3000,
easing: 'linear',
loop: true,
}); <svg viewBox="0 0 260 120" xmlns="http://www.w3.org/2000/svg">
<!-- видимый пунктирный путь -->
<path class="track"
d="M 10,60 C 55,5 95,115 130,60 C 165,5 205,115 250,60" />
<!-- элемент, который едет по пути; cx/cy = начало пути -->
<ellipse class="dot" rx="14" ry="7" cx="10" cy="60" />
</svg> .track {
fill: none;
stroke: #7a90b0;
stroke-width: 2;
stroke-dasharray: 6 5;
opacity: 0.4;
}
.dot {
fill: hsl(270, 80%, 72%);
filter: drop-shadow(0 0 8px hsl(270, 80%, 72%));
transform-box: fill-box; /* transform-origin считается от bbox элемента */
transform-origin: center;
} Морфинг форм
Пять звёзд рейтинга вырастают из крошечной звезды в полноразмерную и одновременно меняют цвет с серого на золотой — одна за другой через stagger . Важное условие для корректного морфинга атрибута d : оба пути должны содержать одинаковое количество точек — здесь по 10 в каждом.
anime({
targets: '.star',
d: [{ value: 'M 24,2 L 29.3,16.7 L 44.9,17.2 L 32.6,26.8 L 36.9,41.8 L 24,33 L 11.1,41.8 L 15.4,26.8 L 3.1,17.2 L 18.7,16.7 Z' }],
fill: 'hsl(45, 95%, 55%)',
duration: 500,
delay: anime.stagger(200),
easing: 'easeOutBack',
direction: 'alternate',
loop: true,
}); <div class="stars-wrap">
<svg class="star-svg" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path class="star" d="M 24,14 L 26.4,20.8 L 33.5,20.9 L 27.8,25.2 L 29.9,32.1 L 24,28 L 18.1,32.1 L 20.2,25.2 L 14.5,20.9 L 21.6,20.8 Z" />
</svg>
<svg class="star-svg" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path class="star" d="M 24,14 L 26.4,20.8 L 33.5,20.9 L 27.8,25.2 L 29.9,32.1 L 24,28 L 18.1,32.1 L 20.2,25.2 L 14.5,20.9 L 21.6,20.8 Z" />
</svg>
<svg class="star-svg" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path class="star" d="M 24,14 L 26.4,20.8 L 33.5,20.9 L 27.8,25.2 L 29.9,32.1 L 24,28 L 18.1,32.1 L 20.2,25.2 L 14.5,20.9 L 21.6,20.8 Z" />
</svg>
<svg class="star-svg" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path class="star" d="M 24,14 L 26.4,20.8 L 33.5,20.9 L 27.8,25.2 L 29.9,32.1 L 24,28 L 18.1,32.1 L 20.2,25.2 L 14.5,20.9 L 21.6,20.8 Z" />
</svg>
<svg class="star-svg" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path class="star" d="M 24,14 L 26.4,20.8 L 33.5,20.9 L 27.8,25.2 L 29.9,32.1 L 24,28 L 18.1,32.1 L 20.2,25.2 L 14.5,20.9 L 21.6,20.8 Z" />
</svg>
</div> .stars-wrap {
display: flex;
justify-content: center;
gap: 8px;
}
.star-svg {
width: 48px;
height: 48px;
}
.star {
fill: hsl(45, 10%, 70%);
} Производительность JS-анимаций: советы
- Анимируйте transform и opacity. Браузер обрабатывает их на GPU, не вызывая reflow. Анимация width , height , top , left — дорогая операция.
- Используйте will-change: transform с умом. Только для элементов, которые анимируются точно и часто — иначе лишняя нагрузка на память.
- Уважайте prefers-reduced-motion . Часть пользователей отключает анимации на системном уровне — медицинские причины, усталость. Проверяйте это медиа-запросом и убирайте анимации или заменяйте мгновенными переходами.
- Pause при уходе вкладки. Подписывайтесь на visibilitychange и вызывайте anim.pause() / anim.play() — иначе анимации продолжают работать вхолостую.
// Уважаем настройку системы
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!prefersReducedMotion) {
anime({ targets: '.hero', opacity: [0, 1], translateY: [30, 0] });
}
// Пауза при скрытой вкладке
const anim = anime({ targets: '.loader', rotate: 360, loop: true });
document.addEventListener('visibilitychange', () => {
document.hidden ? anim.pause() : anim.play();
}); Anime.js — одна из немногих библиотек, которая покрывает весь диапазон задач: от анимации кнопки при ховере до синхронизированного таймлайна с десятками элементов. Маленький размер, чистый API и активная поддержка делают её отличным выбором для любого проекта. Осталось дать волю воображению.
Если хочется анимировать элементы по скроллу без написания JavaScript — посмотрите на AOS.js: анимация при прокрутке.