Создание анимационных пресетов

Анимационные пресеты в Motion One представляют собой способ стандартизации и переиспользования конфигураций анимации, позволяя формировать единый слой абстракции над низкоуровневыми параметрами: длительностью, easing-функциями, трансформациями, ключевыми кадрами и поведением на старте и завершении анимации.

Базовый пресет в Motion One фактически является объектом, который повторяет сигнатуру функции animate. В него включаются параметры:

  • duration
  • delay
  • easing
  • fill
  • direction
  • iterations
  • keyframes
  • transform
  • opacity
  • CSS-свойства

Пресет формируется как неизменяемая конфигурация, которая затем передаётся в разные анимационные вызовы без модификации исходного объекта.

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

Базовая форма пресета

Простейший вариант пресета представляет собой объект-конфигурацию:

const fadeInPreset = {
  keyframes: {
    opacity: [0, 1],
    transform: ["translateY(10px)", "translateY(0px)"]
  },
  duration: 0.4,
  easing: "ease-out"
};

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

Инкапсуляция через фабричные функции

При увеличении количества вариаций статические объекты становятся недостаточными. Применяется подход фабрик пресетов, где параметры параметризуются:

const createFadePreset = (distance = 10, duration = 0.4) => ({
  keyframes: {
    opacity: [0, 1],
    transform: [`translateY(${distance}px)`, "translateY(0px)"]
  },
  duration,
  easing: "ease-out"
});

Фабричный подход позволяет:

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

Композиция пресетов

Сложные интерфейсы требуют объединения нескольких анимационных слоёв. Пресеты начинают рассматриваться как композиционные единицы.

const basePreset = {
  duration: 0.3,
  easing: "ease-out"
};

const fadePreset = {
  keyframes: { opacity: [0, 1] }
};

const slidePreset = {
  keyframes: { transform: ["translateY(20px)", "translateY(0px)"] }
};

const composedPreset = {
  ...basePreset,
  keyframes: {
    ...fadePreset.keyframes,
    ...slidePreset.keyframes
  }
};

При композиции ключевым становится управление конфликтами свойств. Например, transform не может быть безопасно слит через spread-оператор без потери значений, поэтому применяется либо строковая конкатенация трансформаций, либо специализированная функция сборки трансформаций.

Нормализация transform-пресетов

Transform является наиболее чувствительной частью анимации, поскольку включает несколько независимых подпространств:

  • translate
  • scale
  • rotate
  • skew

Корректная стратегия заключается в разбиении transform на атомарные значения:

const transformPreset = {
  translateY: [20, 0],
  scale: [0.95, 1]
};

Далее система сборки пресета преобразует их в единый transform:

const buildTransform = (t) =>
  `translateY(${t.translateY}px) scale(${t.scale})`;

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

Система именованных пресетов

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

const presets = {
  fadeIn: {
    keyframes: { opacity: [0, 1] },
    duration: 0.25,
    easing: "ease-out"
  },
  fadeOut: {
    keyframes: { opacity: [1, 0] },
    duration: 0.2,
    easing: "ease-in"
  },
  pop: {
    keyframes: {
      transform: ["scale(0.9)", "scale(1)"]
    },
    duration: 0.3,
    easing: "spring(1, 80, 10, 0)"
  }
};

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

animate(element, presets.fadeIn);

При этом логика остаётся централизованной, а визуальный язык интерфейса становится консистентным.

Пресеты с параметризацией состояния интерфейса

Часто требуется учитывать состояние UI: hover, active, disabled, loading. Пресеты начинают выступать как описание состояний.

const buttonPresets = {
  idle: {
    scale: 1,
    duration: 0.2
  },
  hover: {
    scale: 1.05,
    duration: 0.15
  },
  active: {
    scale: 0.98,
    duration: 0.1
  }
};

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

const applyState = (el, state) => {
  animate(el, {
    transform: `scale(${buttonPresets[state].scale})`,
    duration: buttonPresets[state].duration
  });
};

Пресеты и временные кривые

Easing-функции в Motion One могут быть как строковыми, так и кастомными кривыми. Пресеты часто включают стандартизированные кривые:

const easingPresets = {
  smooth: "ease-in-out",
  enter: "ease-out",
  exit: "ease-in",
  bounce: "spring(1, 100, 10, 0)"
};

Использование именованных easing-пресетов обеспечивает визуальную согласованность всей системы анимаций.

Многоуровневая архитектура пресетов

При росте системы пресеты разделяются на уровни:

  1. Базовые (primitive)
  2. Семантические (fade, slide, scale)
  3. Компонентные (button, modal, dropdown)
  4. Контекстные (page transition, route change)

Базовый слой определяет физику движения, семантический — тип анимации, компонентный — конкретное поведение UI-элемента, контекстный — глобальные переходы.

const primitives = {
  fast: { duration: 0.15 },
  normal: { duration: 0.3 },
  slow: { duration: 0.6 }
};

const semantic = {
  fadeIn: {
    ...primitives.normal,
    keyframes: { opacity: [0, 1] }
  }
};

const component = {
  modalEnter: {
    ...semantic.fadeIn,
    keyframes: {
      opacity: [0, 1],
      transform: ["scale(0.9)", "scale(1)"]
    }
  }
};

Такой подход снижает энтропию анимационной системы и упрощает её расширение.

Переиспользование через функции-обёртки animate

Motion One предоставляет функцию animate, которая становится базовым исполнителем пресетов. Пресеты не заменяют её, а расширяют:

const runPreset = (el, preset) => animate(el, preset);

runPreset(box, presets.fadeIn);

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

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

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

При работе с массивами элементов пресеты становятся основой stagger-анимаций:

const listPreset = {
  keyframes: {
    opacity: [0, 1],
    transform: ["translateY(15px)", "translateY(0px)"]
  },
  delay: (i) => i * 0.05,
  duration: 0.3
};

Функциональный delay позволяет формировать каскадные эффекты без дополнительного кода управления циклами.

Интеграция пресетов с timeline

В сложных интерфейсах пресеты комбинируются с временными шкалами. Каждый пресет становится сегментом общей анимационной композиции:

timeline([
  [elementA, presets.fadeIn],
  [elementB, presets.slide],
  [elementC, presets.pop]
]);

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

Переопределение пресетов через расширение

Механизм расширения позволяет создавать вариации без копирования:

const fadeInSlow = {
  ...presets.fadeIn,
  duration: 0.8
};

или через функцию:

const extendPreset = (base, overrides) => ({
  ...base,
  ...overrides
});

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

Типизация пресетов

В TypeScript пресеты становятся строго типизированными объектами, что снижает количество ошибок при масштабировании:

type AnimationPreset = {
  keyframes: Record<string, string[] | number[]>;
  duration?: number;
  easing?: string;
  delay?: number | ((i: number) => number);
};

Типизация обеспечивает контроль над структурой пресетов и предотвращает некорректные конфигурации на этапе разработки.

Производственные ограничения и оптимизация

При большом количестве пресетов возникает нагрузка на систему рендеринга. Оптимизация включает:

  • минимизацию количества уникальных keyframes
  • переиспользование easing-функций
  • сокращение числа трансформационных слоёв
  • кэширование вычисляемых пресетов

Особенно критичным становится избегание пересоздания объектов на каждый рендер, поскольку это влияет на garbage collection и производительность анимационного цикла.

Пресеты как язык интерфейса

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

  • появление
  • исчезновение
  • акцентирование
  • переход
  • реакция на взаимодействие

Такой подход делает Motion One не просто инструментом анимации, а системой описания динамики интерфейса через повторяемые структурированные паттерны.