Библиотека anime.js управляет анимацией через изменение свойств объектов. Canvas API, в отличие от DOM, не хранит состояние сцены — отрисовка выполняется вручную на каждом кадре. Интеграция строится на связке:
Ключевой принцип: anime.js не работает напрямую с canvas-элементом, а изменяет JavaScript-объекты, которые затем используются для рендеринга.
<canvas id="scene"></canvas>
const canvas = document.getElementById('scene');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const state = {
x: 100,
y: 100,
radius: 50
};
Объект state выступает связующим звеном между anime.js и
Canvas API.
anime({
targets: state,
x: 500,
duration: 2000,
easing: 'easeInOutQuad',
update: draw
});
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(state.x, state.y, state.radius, 0, Math.PI * 2);
ctx.fill();
}
Ключевые моменты:
targets указывает на объект состоянияupdate вызывается на каждом кадреИспользование update подходит для простых случаев, но
для сложных сцен эффективнее отделить рендеринг:
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawCircle();
requestAnimationFrame(render);
}
render();
А anime.js обновляет только значения:
anime({
targets: state,
x: 600,
duration: 3000,
easing: 'linear'
});
Такой подход:
const particles = Array.from({ length: 50 }, () => ({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
radius: Math.random() * 5 + 2
}));
Анимация:
anime({
targets: particles,
y: () => Math.random() * canvas.height,
delay: anime.stagger(50),
loop: true,
direction: 'alternate'
});
Рендеринг:
function drawParticles() {
particles.forEach(p => {
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
ctx.fill();
});
}
anime.js позволяет анимировать не только числа:
const state = {
color: 'rgb(255, 0, 0)'
};
anime({
targets: state,
color: 'rgb(0, 0, 255)',
duration: 2000,
easing: 'linear'
});
Использование в canvas:
ctx.fillStyle = state.color;
Canvas не имеет встроенной модели трансформаций объектов, поэтому они применяются вручную:
const state = {
x: 200,
y: 200,
rotation: 0
};
anime({
targets: state,
rotation: Math.PI * 2,
duration: 3000,
loop: true,
easing: 'linear'
});
Отрисовка:
function drawRotated() {
ctx.save();
ctx.translate(state.x, state.y);
ctx.rotate(state.rotation);
ctx.fillRect(-50, -50, 100, 100);
ctx.restore();
}
const tl = anime.timeline({
loop: true
});
tl.add({
targets: state,
x: 400,
duration: 1000
}).add({
targets: state,
y: 300,
duration: 1000
}).add({
targets: state,
x: 100,
duration: 1000
});
Преимущества:
Очистка только нужной области
ctx.clearRect(state.x - 60, state.y - 60, 120, 120);
Использование offscreen canvas
const offCanvas = document.createElement('canvas');
const offCtx = offCanvas.getContext('2d');
Позволяет:
anime.js можно связывать с пользовательскими действиями:
canvas.addEventListener('click', (e) => {
anime({
targets: state,
x: e.clientX,
y: e.clientY,
duration: 1000,
easing: 'easeOutElastic'
});
});
Интеграция с физическими расчетами:
const state = {
x: 100,
velocity: 0
};
function updatePhysics() {
state.velocity += 0.5; // гравитация
state.y += state.velocity;
}
Anime.js может управлять начальными условиями:
anime({
targets: state,
velocity: -10,
duration: 500
});
Оптимальная архитектура:
function loop() {
updatePhysics();
draw();
requestAnimationFrame(loop);
}
loop();
anime.js:
Canvas:
1. Отсутствие очистки canvas
Приводит к “шлейфам” и артефактам.
2. Слишком частое создание объектов
// плохо
anime({
targets: { x: 0 }
});
Каждый раз создаётся новый объект.
3. Логика внутри draw()
Рендеринг должен быть максимально “чистым”:
Шейпы и Path2D
const path = new Path2D();
path.arc(0, 0, 50, 0, Math.PI * 2);
Градиенты
const gradient = ctx.createLinearGradient(0, 0, 200, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
Анимация:
anime({
targets: gradient,
// косвенное управление через параметры
});
Рекомендуемая структура:
const state = {};
function update() {}
function render() {}
function loop() {
update();
render();
requestAnimationFrame(loop);
}
Такой подход обеспечивает: