Управление состоянием

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

Ключевыми элементами, формирующими состояние, выступают:

  • Shape / Html / Burst — визуальные сущности
  • Tween — единица анимации
  • Timeline — контейнер для синхронизации
  • Свойства (props) — значения, описывающие текущее состояние

Состояние не хранится централизованно — оно распределено между объектами и их параметрами.


Природа состояния в mo.js

Состояние объекта определяется набором его текущих параметров:

  • геометрия (radius, scale, angle)
  • позиция (x, y)
  • прозрачность (opacity)
  • цвет (fill, stroke)
  • временные характеристики (duration, delay)

Каждое из этих свойств может:

  • быть статическим значением
  • задаваться диапазоном
  • вычисляться динамически через функции

Пример декларативного состояния:

const circle = new mojs.Shape({
  shape: 'circle',
  radius: { 0: 50 },
  fill: 'transparent',
  stroke: 'blue',
  strokeWidth: { 10: 0 },
  duration: 1000
});

Здесь состояние описано как переход от одного значения к другому.


Иммутабельность и декларативный подход

mo.js использует иммутабельную модель описания анимаций:

  • начальные параметры задаются один раз
  • изменения происходят через проигрывание твинов
  • объект не изменяется напрямую в процессе работы

Это означает:

  • отсутствие “ручного” изменения DOM
  • отсутствие необходимости отслеживать состояние вручную
  • состояние = результат вычисления анимации во времени

Динамическое состояние через функции

Любое свойство может быть функцией:

const shape = new mojs.Shape({
  x: () => Math.random() * 300,
  y: () => Math.random() * 300,
  scale: { 0: 1 }
});

Это позволяет:

  • генерировать состояние на лету
  • создавать вариативные анимации
  • избегать глобального хранилища

Timeline как агрегатор состояния

Timeline объединяет несколько анимаций и управляет их синхронизацией.

const timeline = new mojs.Timeline();

timeline.add(circle, anotherShape);
timeline.play();

В этом контексте:

  • состояние всей сцены определяется состояниями входящих элементов
  • Timeline управляет временной координацией
  • отсутствует единый store

Управление состоянием через playback API

mo.js предоставляет API для управления состоянием через методы воспроизведения:

  • .play()
  • .pause()
  • .stop()
  • .replay()
  • .setProgress(progress)

Пример:

circle.setProgress(0.5);

Это устанавливает состояние объекта на середину анимации.

Таким образом:

  • состояние можно позиционировать вручную
  • появляется возможность синхронизации с внешними событиями

Связывание состояния с внешними событиями

Состояние анимации может зависеть от:

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

Пример синхронизации со скроллом:

window.addEventListener('scroll', () => {
  const progress = window.scrollY / 1000;
  circle.setProgress(progress);
});

Это превращает анимацию в функцию от состояния интерфейса.


Интеграция с глобальным состоянием приложения

Хотя mo.js не имеет встроенного хранилища состояния, возможна интеграция с внешними решениями:

  • Redux
  • Zustand
  • Vuex
  • MobX

Пример:

store.subscribe(() => {
  const state = store.getState();
  circle.setProgress(state.animationProgress);
});

mo.js выступает как визуализатор состояния, а не его источник.


Локальное состояние компонентов

Каждый объект mo.js хранит собственное локальное состояние:

  • текущий прогресс
  • текущие значения параметров
  • внутренние вычисления easing

Это состояние:

  • изолировано
  • не доступно напрямую
  • изменяется только через API

Переопределение состояния через tune()

Метод tune() позволяет динамически обновлять параметры:

circle.tune({
  fill: 'red',
  radius: 100
});

Особенности:

  • не пересоздаёт объект
  • обновляет внутреннее состояние
  • может применяться на лету

Состояние и easing-функции

Состояние анимации зависит не только от значений, но и от easing-функций:

const shape = new mojs.Shape({
  scale: { 0: 1 },
  easing: 'elastic.out'
});

Easing определяет:

  • как именно изменяется состояние
  • нелинейную динамику переходов
  • визуальное восприятие

Сложные состояния через Burst

Burst генерирует множество элементов с независимыми состояниями:

const burst = new mojs.Burst({
  count: 10,
  radius: { 0: 100 }
});

Каждый элемент:

  • имеет собственное состояние
  • рассчитывается автоматически
  • управляется общими параметрами

Это позволяет создавать коллективное состояние, состоящее из множества объектов.


Состояние и последовательности анимаций

Комбинирование анимаций формирует сложные состояния:

const tl = new mojs.Timeline();

tl.add(
  new mojs.Shape({ scale: { 0: 1 } }),
  new mojs.Shape({ x: { 0: 100 }, delay: 300 })
);

Здесь:

  • состояние сцены меняется поэтапно
  • каждый tween влияет на итоговую композицию
  • последовательность формирует поведение

Контроль состояния через события

mo.js поддерживает хуки:

circle.then({
  onComplete() {
    console.log('done');
  }
});

События позволяют:

  • реагировать на изменения состояния
  • запускать новые анимации
  • строить цепочки

Состояние и производительность

Особенности управления состоянием в mo.js:

  • отсутствие постоянных мутаций DOM
  • расчёты происходят внутри requestAnimationFrame
  • минимизация лишних перерисовок

Это делает подход:

  • предсказуемым
  • оптимизированным
  • удобным для сложных интерфейсов

Композиция состояний

Сложные интерфейсы строятся через композицию:

  • множество Shape
  • объединение в Timeline
  • управление через внешний контроллер

Каждый уровень добавляет:

  • детализацию состояния
  • гибкость
  • масштабируемость

Детерминированность состояния

При одинаковых параметрах mo.js гарантирует:

  • одинаковый результат
  • повторяемость анимации
  • предсказуемое поведение

Это особенно важно для:

  • тестирования
  • синхронизации
  • UI-логики

Управление состоянием в реактивных интерфейсах

В связке с React/Vue:

  • состояние хранится во фреймворке
  • mo.js реагирует на изменения
  • анимация становится производной от состояния

Пример (React-подход):

useEffect(() => {
  circle.setProgress(progress);
}, [progress]);

Итеративное изменение состояния

mo.js позволяет постепенно изменять состояние:

  • через setProgress
  • через tune
  • через chaining (then())

Это создаёт гибкую модель:

  • начальное состояние
  • промежуточные состояния
  • финальное состояние

Синхронизация нескольких состояний

Для сложных сцен:

  • несколько Timeline
  • общий контроллер
  • синхронизация через progress
timeline1.setProgress(p);
timeline2.setProgress(p);

Это обеспечивает:

  • единое состояние интерфейса
  • согласованную анимацию
  • контроль на уровне приложения

Ограничения модели состояния

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

  • нет встроенного глобального состояния
  • нет реактивности “из коробки”
  • сложность при больших приложениях без архитектуры

Однако:

  • высокая гибкость
  • точный контроль
  • независимость от фреймворков

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

Эффективная схема:

  1. Хранение состояния во внешнем слое (например, store)
  2. Преобразование состояния в параметры mo.js
  3. Управление прогрессом и параметрами через API
  4. Синхронизация через события или эффекты

Такой подход разделяет:

  • логику состояния
  • визуальное представление

и делает систему масштабируемой.