В mo.js любая анимация строится вокруг набора параметров, передаваемых в объект конфигурации. Эти параметры определяют геометрию, временные характеристики, поведение и визуальные свойства объектов. Ключевая особенность библиотеки заключается в том, что большинство параметров может быть изменено после создания экземпляра анимации, что открывает механизм реактивного управления сценой.
Создание объекта анимации фиксирует начальное состояние, но не фиксирует поведение навсегда. Экземпляры сохраняют внутренние ссылки на параметры, что позволяет модифицировать их без пересоздания всей анимации.
const circle = new mojs.Shape({
shape: 'circle',
radius: 40,
fill: 'cyan',
duration: 1000
});
После создания такой структуры параметры доступны для изменения через API экземпляра или через методы управления прогрессом анимации.
Основной механизм реактивного обновления в mo.js реализуется через
метод tune(). Этот метод позволяет переопределять
конфигурацию уже созданной анимации без её пересоздания.
circle.tune({
radius: 80,
fill: 'orange'
});
Поведение метода:
Внутренне происходит частичное слияние конфигураций, где новые значения перезаписывают старые, а остальные остаются неизменными.
mo.js поддерживает изменение параметров в процессе выполнения анимации. Это особенно важно для интерактивных интерфейсов, где состояние визуального элемента зависит от внешних событий.
Пример изменения параметров в реальном времени:
document.addEventListener('mousemove', (e) => {
circle.tune({
x: e.clientX,
y: e.clientY
});
});
Такой подход превращает анимацию в реактивный объект, который не просто воспроизводится, а непрерывно адаптируется к входным данным.
Особенность модели:
Реактивность в mo.js часто используется совместно с состоянием интерфейса. В отличие от классических анимационных библиотек, где изменение состояния требует пересоздания сцены, здесь достаточно обновить параметры экземпляра.
Типичный сценарий:
let scale = 1;
window.addEventListener('wheel', (e) => {
scale += e.deltaY * -0.001;
circle.tune({
scale: scale
});
});
Таким образом анимация становится проекцией состояния, а не статической последовательностью кадров.
Несмотря на гибкость, обновление параметров имеет архитектурные ограничения, связанные с внутренним таймингом и системой tweening:
Пример проблемного изменения:
circle.tune({
duration: 3000
});
Если анимация уже находится на середине, новое значение duration не перераспределяет уже прошедшие кадры, что может привести к визуальной несогласованности.
mo.js поддерживает два уровня анимационных сущностей:
Реактивное обновление работает по-разному в каждом случае.
Для tween:
const tween = new mojs.Tween({
duration: 1000,
onUpdate(progress) {
circle.setProgress(progress);
}
});
Для timeline:
const timeline = new mojs.Timeline();
timeline.add(circle, anotherShape);
При изменении параметров внутри timeline необходимо учитывать, что:
tune() не всегда синхронизирует вложенные элементы
автоматическиОдним из ключевых архитектурных принципов является трактовка параметров как зависимостей от внешних данных. В этом контексте mo.js можно рассматривать как систему, где параметры анимации вычисляются на основе состояния приложения.
Пример зависимости от внешнего значения:
function updateFromState(state) {
circle.tune({
radius: 20 + state.level * 10,
opacity: state.active ? 1 : 0.3
});
}
Такой подход позволяет:
Внутренне tune() реализует частичное обновление
конфигурации, что концептуально близко к diff-подходу. Вместо полной
перезаписи объекта происходит:
Это позволяет минимизировать накладные расходы и избежать лишних пересчётов анимационной кривой.
При построении сложных сцен часто требуется каскадное обновление
нескольких анимаций. В mo.js это реализуется вручную через
последовательные вызовы tune().
circle.tune({ radius: 60 });
ring.tune({ strokeWidth: 4 });
burst.tune({ count: 12 });
При необходимости синхронизации можно объединять изменения в один логический блок, что снижает вероятность промежуточных визуальных артефактов.
Наиболее сильное применение реактивности проявляется в интерактивных системах, где пользовательский ввод напрямую изменяет анимационные параметры.
Пример управления через клавиатуру:
window.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp') {
circle.tune({
radius: circle._props.radius + 10
});
}
});
Такая модель позволяет:
При вызове tune() происходит обновление внутреннего
состояния объекта. Это включает:
Важно, что сам цикл анимации не перезапускается, а продолжает работать в текущем временном контексте, что обеспечивает плавность переходов.
При наличии задержек в анимации реактивные изменения могут влиять только на будущие сегменты выполнения.
const shape = new mojs.Shape({
radius: { 0: 50 },
duration: 1000,
delay: 500
});
Если изменить параметры во время delay-фазы, новые значения применятся только при старте фактической анимации, что создаёт возможность предварительной настройки поведения.
Реактивный подход позволяет строить эффекты, зависящие от множества факторов:
Пример зависимости от скорости движения:
let lastX = 0;
window.addEventListener('mousemove', (e) => {
const speed = Math.abs(e.clientX - lastX);
circle.tune({
radius: 30 + speed
});
lastX = e.clientX;
});
Такая модель превращает анимацию в непрерывно адаптирующуюся систему, где визуальные параметры являются функцией от входного сигнала.