Бесконечная анимация

Snap.svg предоставляет мощный инструмент для работы с анимацией векторной графики на базе SVG. Одной из наиболее востребованных возможностей является организация бесконечной или циклической анимации, когда объект повторяет движения или трансформации без остановки.


Основные принципы бесконечной анимации

Для реализации бесконечного цикла анимации используется комбинация методов animate и функций обратного вызова. Snap.svg позволяет анимировать любые атрибуты SVG: координаты, размеры, цвета, прозрачность, трансформации.

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

var s = Snap("#svg");
var circle = s.circle(50, 50, 30).attr({
    fill: "#3498db"
});

function animateCircle() {
    circle.animate({ cx: 250 }, 1000, mina.easeinout, function() {
        circle.animate({ cx: 50 }, 1000, mina.easeinout, animateCircle);
    });
}

animateCircle();

В этом примере:

  • Объект circle перемещается по оси X от 50 до 250 за 1 секунду.
  • После завершения анимации вызывается обратный вызов, в котором объект возвращается на исходную позицию.
  • Функция animateCircle рекурсивно перезапускает анимацию, создавая бесконечный цикл движения.

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

Snap.svg использует собственную библиотеку интерполяции mina для плавного перехода значений. Доступны стандартные функции:

  • mina.linear – линейная анимация без ускорения
  • mina.easein – постепенный старт
  • mina.easeout – постепенная остановка
  • mina.easeinout – плавное начало и завершение

Применение разных интерполяторов позволяет создавать более естественные циклические движения и плавные эффекты.

circle.animate({ cy: 200 }, 1500, mina.easeinout, function() {
    circle.animate({ cy: 50 }, 1500, mina.easeinout, animateCircle);
});

Анимация нескольких атрибутов одновременно

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

circle.animate({ cx: 250, cy: 200, r: 50, fill: "#e74c3c" }, 2000, mina.easeinout, function() {
    circle.animate({ cx: 50, cy: 50, r: 30, fill: "#3498db" }, 2000, mina.easeinout, animateCircle);
});

Здесь одновременно меняются:

  • Положение (cx, cy)
  • Размер (r)
  • Цвет заливки (fill)

Таким образом, можно создавать эффект живого, пульсирующего объекта, который непрерывно трансформируется.


Циклические анимации с помощью repeat и setTimeout

Для удобства иногда используют функцию setTimeout или setInterval, чтобы запускать анимацию через определённые интервалы. Это полезно для синхронизации нескольких объектов:

function bounceRect(rect) {
    rect.animate({ y: 150 }, 800, mina.bounce, function() {
        rect.animate({ y: 50 }, 800, mina.bounce, function() {
            setTimeout(function() {
                bounceRect(rect);
            }, 0);
        });
    });
}

var rect = s.rect(100, 50, 80, 80).attr({ fill: "#2ecc71" });
bounceRect(rect);

Использование setTimeout гарантирует, что новая анимация запустится после завершения предыдущей, создавая непрерывный эффект “прыжка”.


Анимация с использованием Snap.animate

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

Snap.animate(0, 360, function(val) {
    circle.attr({ transform: `rotate(${val}, 150, 150)` });
}, 3000, mina.linear, function() {
    Snap.animate(360, 0, function(val) {
        circle.attr({ transform: `rotate(${val}, 150, 150)` });
    }, 3000, mina.linear, arguments.callee);
});
  • Значение val интерполируется от 0 до 360 градусов.
  • После завершения цикла вызывается рекурсивная функция, создавая бесконечное вращение.

Использование групп для сложных циклов

Snap.svg позволяет объединять несколько элементов в группу через g(). Это упрощает управление анимацией сразу нескольких объектов:

var group = s.g(
    s.circle(50, 50, 20).attr({ fill: "#f1c40f" }),
    s.rect(100, 50, 40, 40).attr({ fill: "#e67e22" })
);

function animateGroup() {
    group.animate({ transform: "t200,0" }, 1000, mina.easeinout, function() {
        group.animate({ transform: "t0,0" }, 1000, mina.easeinout, animateGroup);
    });
}

animateGroup();
  • Преобразования применяются ко всей группе одновременно.
  • Позволяет создавать сложные циклические сцены, где все элементы синхронизированы.

Бесконечные градиенты и цвета

Циклическая анимация может распространяться и на градиенты или цвета заливки, используя атрибуты fill или stroke:

var rect = s.rect(50, 50, 100, 100);
function animateColor() {
    rect.animate({ fill: "#9b59b6" }, 1000, mina.easeinout, function() {
        rect.animate({ fill: "#1abc9c" }, 1000, mina.easeinout, animateColor);
    });
}
animateColor();

Цвет плавно меняется, создавая бесконечное переливание.


Рекомендации по оптимизации

  • Избегать слишком коротких интервалов и рекурсий без паузы, чтобы не перегружать браузер.
  • Для сложных сцен использовать requestAnimationFrame в комбинации с Snap.animate для плавного FPS.
  • Группировать элементы в g() для массовой анимации, это снижает количество отдельных вызовов animate.
  • Использовать функции mina для естественного движения, избегая резких линейных изменений.

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