Концепция модулей в mo.js

Библиотека mo.js построена вокруг модульного подхода, где каждая сущность анимации представлена отдельным, изолированным компонентом. Такой подход позволяет создавать сложные анимации, комбинируя простые элементы без жёсткой связности между ними.

Модули в mo.js — это не просто файлы или классы, а логические единицы, каждая из которых отвечает за конкретный аспект анимации: геометрию, временные параметры, эффекты или взаимодействие.


Ключевые принципы модульности

1. Изоляция ответственности Каждый модуль выполняет строго определённую задачу:

  • создание формы
  • управление временем
  • вычисление значений
  • применение эффектов

Это снижает сложность и упрощает отладку.

2. Переиспользуемость Один и тот же модуль может использоваться в разных анимациях без изменений.

3. Композиция вместо наследования mo.js активно использует композицию: сложные анимации создаются путём объединения модулей, а не расширения классов.

4. Декларативный стиль Анимации описываются через параметры, а не через последовательность императивных действий.


Основные типы модулей

Shape-модули

Отвечают за создание и отображение визуальных объектов.

Пример:

const circle = new mojs.Shape({
  shape: 'circle',
  radius: 50,
  fill: 'red'
});

Ключевые свойства:

  • shape — тип фигуры
  • radius, width, height — размеры
  • fill, stroke — стили

Каждый Shape — независимый модуль, который можно анимировать отдельно.


Tween-модули

Управляют временной интерполяцией значений.

const tween = new mojs.Tween({
  duration: 1000,
  onUpdate: (progress) => {
    console.log(progress);
  }
});

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

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

Burst-модули

Создают сложные анимации частиц.

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

Комбинируют в себе:

  • множество Shape-модулей
  • общий таймлайн
  • распределение параметров

Timeline-модули

Организуют последовательность и синхронизацию анимаций.

const timeline = new mojs.Timeline();
timeline.add(circle, burst);
timeline.play();

Функции:

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

Внутренние служебные модули

Easing-функции

Определяют характер изменения значений во времени.

mojs.easing.ease.out

Модульный подход позволяет:

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

Delta-модули

Отвечают за интерполяцию значений между начальным и конечным состоянием.

Пример:

radius: { 0: 100 }

Здесь создаётся внутренний модуль, который:

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

Stagger-механизм

Позволяет распределять значения между множеством элементов:

delay: 'stagger(100, 0)'

Это тоже отдельный модуль, который:

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

Связь между модулями

Модули взаимодействуют через:

  • общие параметры
  • события (onStart, onUpdate, onComplete)
  • таймлайны

Важно, что связь остаётся слабой:

  • модули не знают внутренности друг друга
  • взаимодействие происходит через API

Жизненный цикл модуля

Каждый модуль проходит несколько этапов:

1. Инициализация

  • создание объекта
  • разбор параметров

2. Подготовка

  • вычисление стартовых значений
  • настройка внутренних зависимостей

3. Запуск

  • активация таймера
  • начало анимации

4. Обновление

  • пересчёт значений на каждом кадре
  • применение изменений

5. Завершение

  • остановка
  • вызов callback-функций

Расширяемость модулей

mo.js позволяет создавать собственные модули, используя базовые классы.

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

class CustomTween extends mojs.Tween {
  constructor(options) {
    super(options);
  }

  _update(progress) {
    // кастомная логика
  }
}

Возможности расширения:

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

Декларативная конфигурация как модуль

Даже параметры в mo.js рассматриваются как мини-модули:

x: { 0: 200, easing: 'cubic.out' }

Здесь:

  • значение — это не просто число
  • это объект с логикой изменения
  • внутри создаётся модуль интерполяции

Композиция модулей в сложных анимациях

Пример объединения:

const shape = new mojs.Shape({ radius: { 0: 50 } });

const burst = new mojs.Burst({ count: 5 });

const timeline = new mojs.Timeline()
  .add(shape, burst)
  .play();

Здесь:

  • Shape и Burst — независимые модули
  • Timeline — управляющий модуль
  • результат — сложная анимация из простых частей

Преимущества модульного подхода в mo.js

  • Гибкость — легко изменять отдельные части
  • Масштабируемость — сложные сцены собираются из простых элементов
  • Повторное использование — модули применяются в разных проектах
  • Читаемость кода — логика разбита на понятные блоки
  • Тестируемость — каждый модуль можно проверять отдельно

Типичные ошибки при работе с модулями

Сильная связность

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

Избыточная логика в одном модуле

  • нарушение принципа единственной ответственности

Игнорирование Timeline

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

Неправильное использование параметров

  • смешивание декларативного и императивного подходов

Практический паттерн организации кода

Структура проекта:

animations/
  shapes/
  bursts/
  timelines/
  utils/

Каждый файл:

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

Пример:

// shapes/circle.js
export const createCircle = () =>
  new mojs.Shape({
    shape: 'circle',
    radius: { 0: 50 }
  });

Взаимодействие модулей через события

const shape = new mojs.Shape({
  onComplete() {
    console.log('done');
  }
});

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

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

Итоговая модель

Модульная система mo.js представляет собой:

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

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