Последовательные анимации

Snap.svg предоставляет удобный API для создания и управления анимациями SVG-элементов. Последовательные анимации позволяют выполнять несколько анимаций одну за другой, формируя сложные визуальные эффекты. В основе лежит метод animate, который можно комбинировать с колбэками и промисами для строгого порядка исполнения.


Метод animate и его параметры

Метод animate имеет следующую сигнатуру:

element.animate(attrs, duration, easing, callback);
  • attrs — объект с конечными значениями атрибутов SVG, например: { cx: 200, cy: 100, r: 50 }.
  • duration — длительность анимации в миллисекундах.
  • easing — функция интерполяции (линейная, синусоидальная и т.д.).
  • callback — функция, выполняемая после завершения анимации.

Пример базовой анимации:

var circle = s.circle(50, 50, 40);
circle.animate({ cx: 200 }, 1000, mina.linear, function() {
    console.log('Анимация завершена');
});

Последовательность с использованием колбэков

Чтобы выполнить несколько анимаций подряд, можно вложить вызовы animate в колбэки:

circle.animate({ cx: 200 }, 1000, mina.easein, function() {
    circle.animate({ cy: 150 }, 1000, mina.easeout, function() {
        circle.animate({ r: 80 }, 800, mina.elastic);
    });
});

Особенности подхода:

  • Полная контрольность над порядком.
  • Можно задавать разные функции easing для каждой анимации.
  • Недостаток — глубокая вложенность при большом числе анимаций.

Использование mina для плавных переходов

Snap.svg интегрирован с библиотекой mina, предоставляющей предопределённые функции интерполяции:

  • mina.linear — равномерное изменение.
  • mina.easein — ускорение к концу анимации.
  • mina.easeout — замедление к концу.
  • mina.elastic — пружинный эффект.
  • mina.bounce — имитация отскока.

Пример:

circle.animate({ cx: 300 }, 1200, mina.bounce, function() {
    circle.animate({ cy: 200 }, 800, mina.elastic);
});

Последовательные анимации через цепочки промисов

Для уменьшения вложенности используется оборачивание анимаций в промисы:

function animateElement(el, attrs, duration, easing) {
    return new Promise(resolve => {
        el.animate(attrs, duration, easing, resolve);
    });
}

animateElement(circle, { cx: 200 }, 1000, mina.easein)
    .then(() => animateElement(circle, { cy: 150 }, 1000, mina.easeout))
    .then(() => animateElement(circle, { r: 80 }, 800, mina.elastic));

Преимущества промисов:

  • Читаемый код без вложенных колбэков.
  • Лёгкая интеграция с async/await.
  • Возможность динамически строить последовательность.

Асинхронные функции и await

С использованием async/await код становится ещё более линейным:

async function runSequence() {
    await animateElement(circle, { cx: 250 }, 1000, mina.easein);
    await animateElement(circle, { cy: 180 }, 1000, mina.easeout);
    await animateElement(circle, { r: 60 }, 800, mina.elastic);
}

runSequence();

Сочетание параллельных и последовательных анимаций

Иногда требуется выполнять несколько анимаций одновременно, а затем переходить к следующей группе:

async function complexAnimation() {
    await Promise.all([
        animateElement(circle, { cx: 200 }, 1000, mina.easein),
        animateElement(rect, { y: 150 }, 1000, mina.easeout)
    ]);
    await animateElement(circle, { r: 90 }, 800, mina.elastic);
}

complexAnimation();
  • Promise.all запускает анимации параллельно.
  • Последующие await обеспечивают последовательность групп анимаций.

Работа с временными задержками

Для создания пауз между анимациями удобно использовать функцию-таймер:

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function sequenceWithDelay() {
    await animateElement(circle, { cx: 200 }, 1000, mina.easein);
    await delay(500);
    await animateElement(circle, { cy: 150 }, 1000, mina.easeout);
}

Итоговые рекомендации по построению последовательностей

  • Использовать промисы или async/await для упрощения чтения кода.
  • Разделять анимации на логические группы при сложных последовательностях.
  • Применять разные функции easing для создания выразительных эффектов.
  • Для синхронизации нескольких элементов применять Promise.all.
  • Использовать таймеры для промежуточных пауз и контроля ритма анимаций.

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