Переиспользование анимаций

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

Переиспользование в mo.js строится не вокруг «готовых эффектов», а вокруг фабрик, конфигов и экземпляров анимаций, которые можно многократно создавать, модифицировать и запускать.

Конфигурация как базовый строительный блок

Наиболее простая форма переиспользования — выделение конфигурации в отдельную переменную.

const baseOptions = {
  left: 0,
  top: 0,
  radius: { 0: 50 },
  duration: 600,
  easing: 'cubic.out',
  fill: 'transparent',
  stroke: '#4F80FF',
  strokeWidth: 2
};

const circle = new mojs.Shape({
  ...baseOptions,
  shape: 'circle'
});

const square = new mojs.Shape({
  ...baseOptions,
  shape: 'rect'
});

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


Фабрики анимаций

Следующий уровень переиспользования — функции-фабрики, возвращающие новые экземпляры анимаций.

function createPulse(color = '#4F80FF', radius = 40) {
  return new mojs.Shape({
    shape: 'circle',
    fill: 'transparent',
    stroke: color,
    strokeWidth: 2,
    radius: { 0: radius },
    duration: 500,
    easing: 'quad.out'
  });
}

Такой подход решает сразу несколько задач:

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

Использование:

const pulse1 = createPulse('#FF4F4F', 60);
const pulse2 = createPulse('#4F80FF', 40);

pulse1.play();
pulse2.play();

Фабрики особенно полезны при построении UI-систем, где одинаковые эффекты используются в разных компонентах.


Переиспользование через методы .replay() и .reset()

mo.js предоставляет встроенные механизмы повторного запуска анимаций без пересоздания экземпляра.

const burst = new mojs.Burst({
  radius: { 0: 100 },
  count: 8,
  children: {
    shape: 'circle',
    radius: 10,
    fill: '#4F80FF'
  }
});

burst.play();

Повторное использование:

burst.replay();

Метод .replay():

  • сбрасывает текущее состояние
  • запускает анимацию заново
  • сохраняет ссылку на тот же объект

Метод .reset() используется для возврата в начальное состояние без запуска:

burst.reset();

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


Переиспользуемые таймлайны

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

const circle = new mojs.Shape({
  shape: 'circle',
  radius: { 0: 40 },
  fill: 'none',
  stroke: '#4F80FF'
});

const square = new mojs.Shape({
  shape: 'rect',
  radius: { 0: 30 },
  fill: 'none',
  stroke: '#FF4F4F'
});

const timeline = new mojs.Timeline({
  repeat: 0
});

timeline.add(circle, square);

Теперь весь набор анимаций управляется централизованно:

timeline.replay();

Переиспользование таймлайнов особенно эффективно в интерфейсах с состояниями:

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

Клонирование анимаций через функции

Прямого глубокого клонирования в mo.js нет, поэтому распространённый паттерн — создание «чистых» фабрик.

function createRing({ color, radius, duration }) {
  return new mojs.Shape({
    shape: 'circle',
    fill: 'transparent',
    stroke: color,
    strokeWidth: 2,
    radius: { 0: radius },
    duration
  });
}

Использование:

const successRing = createRing({
  color: '#2ECC71',
  radius: 80,
  duration: 700
});

const errorRing = createRing({
  color: '#E74C3C',
  radius: 80,
  duration: 700
});

Такой подход заменяет необходимость копирования объектов и снижает риск ошибок, связанных с состоянием экземпляров.


Переиспользование через общие базовые пресеты

В больших проектах целесообразно выделять глобальные пресеты анимаций.

const presets = {
  fastFade: {
    duration: 200,
    easing: 'linear.none'
  },

  smoothPop: {
    duration: 600,
    easing: 'cubic.out'
  },

  elastic: {
    duration: 900,
    easing: 'elastic.out'
  }
};

Использование:

const pop = new mojs.Shape({
  ...presets.smoothPop,
  shape: 'circle',
  radius: { 0: 50 },
  fill: '#4F80FF'
});

Преимущества такого подхода:

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

Динамическое переиспользование через .tune()

Метод .tune() позволяет изменять параметры уже созданной анимации без её пересоздания.

const shape = new mojs.Shape({
  shape: 'circle',
  radius: 40,
  fill: '#4F80FF'
});

Изменение параметров перед повторным запуском:

shape.tune({
  fill: '#FF4F4F',
  radius: 60
}).replay();

Это особенно важно в сценариях, где один и тот же эффект применяется к разным элементам UI.


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

Когда требуется массовое управление анимациями, используется хранение экземпляров в структурах данных.

const animations = [];

document.querySelectorAll('.dot').forEach((el) => {
  const anim = new mojs.Shape({
    el,
    shape: 'circle',
    radius: { 0: 20 },
    duration: 400
  });

  animations.push(anim);
});

Теперь можно управлять всеми анимациями централизованно:

animations.forEach(anim => anim.replay());

Такой подход используется в системах:

  • интерактивных графиков
  • сеток элементов
  • игровых интерфейсов

Композиция переиспользуемых эффектов

Сильнейший способ переиспользования — создание составных эффектов из базовых анимаций.

function createSuccessEffect() {
  const burst = new mojs.Burst({
    radius: { 0: 100 },
    count: 10,
    children: {
      shape: 'circle',
      fill: '#2ECC71',
      radius: 8
    }
  });

  const ring = new mojs.Shape({
    shape: 'circle',
    radius: { 0: 80 },
    fill: 'transparent',
    stroke: '#2ECC71',
    strokeWidth: 3
  });

  return new mojs.Timeline().add(burst, ring);
}

Использование:

const success = createSuccessEffect();
success.replay();

Композиция позволяет создавать высокоуровневые эффекты, не теряя контроля над низкоуровневыми деталями.


Переиспользование через события интерфейса

Часто анимации связываются с DOM-событиями и используются многократно без пересоздания.

const clickAnim = new mojs.Shape({
  shape: 'circle',
  radius: { 0: 30 },
  fill: '#4F80FF',
  duration: 400
});

button.addEventListener('click', () => {
  clickAnim.replay();
});

Такой подход уменьшает накладные расходы и повышает отзывчивость интерфейса.


Изоляция логики анимаций

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

const animationsMap = {
  success: createSuccessEffect,
  error: () => new mojs.Shape({
    shape: 'circle',
    radius: { 0: 60 },
    stroke: '#E74C3C'
  })
};

Использование:

function playAnimation(type) {
  const animFactory = animationsMap[type];
  const anim = animFactory();
  anim.replay();
}

Это создаёт гибкую систему, где добавление нового эффекта не затрагивает существующий код.


Переиспользование через конфигурационные слои

Для сложных систем применяется многослойная конфигурация:

const base = {
  duration: 500,
  easing: 'cubic.out'
};

const interactive = {
  ...base,
  duration: 300
};

const emphasis = {
  ...base,
  duration: 900,
  easing: 'elastic.out'
};

Каждый слой наследует предыдущий, создавая управляемую иерархию параметров.


Переиспользование как принцип архитектуры

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

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

Такой подход позволяет строить сложные интерфейсы, в которых анимация становится частью системной логики, а не изолированным визуальным украшением