При работе с библиотекой Vivus каждая SVG-анимация рассматривается как независимый исполнитель, управляемый собственным экземпляром класса. Базовая модель предполагает, что каждый объект живёт в своём временном цикле, однако в реальных интерфейсах часто требуется согласованное поведение нескольких SVG одновременно: логотипы, составные иллюстрации, инфографика, сложные сцены.
Синхронизация в этом контексте означает не просто одновременный старт, а согласованное управление фазами анимации: запуск, прогресс, завершение и повторное воспроизведение.
Ключевой момент — отсутствие встроенного глобального таймлайна. Это означает, что координация полностью переносится на уровень логики приложения.
Каждый SVG-элемент инициализируется отдельным экземпляром:
const iconA = new Vivus('iconA', {
type: 'delayed',
duration: 120,
start: 'manual'
});
const iconB = new Vivus('iconB', {
type: 'delayed',
duration: 120,
start: 'manual'
});
Установка start: 'manual' критична: автоматический
запуск разрушает возможность синхронизации, так как анимации начинают
существовать в разных временных точках.
Далее управление переносится в пользовательский слой:
iconA.play();
iconB.play();
На этом уровне достигается лишь поверхностная синхронность — старт в одном кадре, но без гарантии совпадения прогресса на протяжении всей анимации.
Даже при одинаковых параметрах duration и
type возможны расхождения:
В результате анимации начинают «расходиться» уже после первых 20–30% прогресса.
Для устранения этого эффекта требуется переход к координированному управлению состоянием.
Простейший уровень синхронизации строится через единый управляющий вызов:
function startAll() {
iconA.play();
iconB.play();
iconC.play();
}
requestAnimationFrame(startAll);
Использование requestAnimationFrame позволяет запустить
все экземпляры в одном визуальном кадре, минимизируя рассинхрон
старта.
Однако такой подход не управляет фазами выполнения и не контролирует завершение.
Более точная модель строится через обработку завершения анимации. Vivus поддерживает callback завершения:
const iconA = new Vivus('iconA', {
duration: 100,
start: 'manual'
}, onComplete);
function onComplete() {
console.log('Animation finished');
}
При нескольких элементах используется счётчик завершений:
let completed = 0;
const total = 3;
function handleComplete() {
completed++;
if (completed === total) {
allFinished();
}
}
function allFinished() {
console.log('All animations synchronized end');
}
new Vivus('iconA', { duration: 100, start: 'manual' }, handleComplete);
new Vivus('iconB', { duration: 100, start: 'manual' }, handleComplete);
new Vivus('iconC', { duration: 100, start: 'manual' }, handleComplete);
requestAnimationFrame(() => {
iconA.play();
iconB.play();
iconC.play();
});
Здесь достигается синхронизация финальной точки, но не промежуточного состояния.
Для более строгой координации вводится внешний таймер, который управляет всеми экземплярами как группой.
Идея заключается в том, чтобы не позволять каждому Vivus жить в своём темпе, а обновлять их состояние из общего источника времени.
const instances = [iconA, iconB, iconC];
let startTime = null;
const duration = 2000;
function animate(timestamp) {
if (!startTime) startTime = timestamp;
const progress = Math.min((timestamp - startTime) / duration, 1);
instances.forEach(instance => {
instance.setFrameProgress(progress);
});
if (progress < 1) {
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate);
Метод setFrameProgress в Vivus не является публичным в
классической API-версии, поэтому в реальных проектах используется либо
модифицированная сборка, либо управление через внутренние методы
(stop, play, reset) в комбинации
с сегментацией.
Vivus поддерживает несколько режимов отрисовки:
delayedsynconeByOneПри синхронизации нескольких элементов ключевым становится выбор одинакового типа.
const commonConfig = {
type: 'sync',
duration: 150,
start: 'manual'
};
const iconA = new Vivus('iconA', commonConfig);
const iconB = new Vivus('iconB', commonConfig);
Режим sync обеспечивает одновременную отрисовку всех
путей внутри одного SVG. При использовании на нескольких элементах он
снижает визуальный разрыв между объектами, так как поведение становится
более предсказуемым.
Иногда требуется не абсолютная одновременность, а управляемая последовательность с минимальными задержками.
iconA.play();
iconA.getStatus = function () {
return this.getStatus ? this.getStatus() : 'unknown';
};
Практический вариант строится через цепочку callback:
new Vivus('iconA', { start: 'manual' }, () => {
new Vivus('iconB', { start: 'manual' }, () => {
new Vivus('iconC', { start: 'manual' });
}).play();
}).play();
Такой подход создаёт эффект связанной сцены, где элементы раскрываются по очереди, но сохраняют единый ритм.
Даже при идентичной длительности визуальная синхронность может нарушаться из-за различий в SVG:
Для устранения дисбаланса применяется нормализация:
const config = {
type: 'delayed',
duration: 120,
animTimingFunction: Vivus.EASE
};
Уменьшение вариативности easing-функций помогает добиться визуального совпадения фаз.
Для масштабных сцен вводится единая структура управления экземплярами:
class VivusGroup {
constructor(instances) {
this.instances = instances;
}
playAll() {
requestAnimationFrame(() => {
this.instances.forEach(i => i.play());
});
}
stopAll() {
this.instances.forEach(i => i.stop());
}
resetAll() {
this.instances.forEach(i => i.reset());
}
}
Использование группы позволяет свести синхронизацию к одному управляющему объекту, устраняя необходимость ручного контроля каждого экземпляра.
Часто запуск анимации должен совпадать с внешними событиями интерфейса:
window.addEventListener('load', () => {
group.playAll();
});
При работе с прокруткой добавляется контроль видимости:
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
group.playAll();
}
});
});
observer.observe(document.querySelector('#scene'));
Повторная синхронизация требует полного сброса состояния:
group.resetAll();
requestAnimationFrame(() => {
group.playAll();
});
Без явного reset возможны остаточные состояния stroke,
что нарушает повторяемость сцены.
При увеличении количества элементов основная проблема смещается от старта к накоплению микрозадержек. Для компенсации используются следующие приёмы:
requestAnimationFrame для стартаТакая стратегия позволяет удерживать визуальную синхронность даже при десятках элементов, формируя цельную анимированную композицию без заметных рассогласований между SVG-компонентами