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

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

Наиболее частая ошибка при работе с анимациями — дублирование конфигураций. Вместо этого используется подход с общими параметрами, которые передаются в фабрики.

import { animate } from "popmotion";

const baseSpring = {
  type: "spring",
  stiffness: 200,
  damping: 20
};

animate({
  from: 0,
  to: 100,
  ...baseSpring
});

animate({
  from: 200,
  to: 300,
  ...baseSpring
});

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


Фабрики анимаций как способ абстракции

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

import { animate } from "popmotion";

const createFadeAnimation = (from = 0, to = 1, duration = 300) => ({
  from,
  to,
  duration,
  ease: t => t
});

const fadeIn = (element) => {
  animate({
    ...createFadeAnimation(0, 1, 400),
    onUpdate: v => {
      element.style.opacity = v;
    }
  });
};

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


Композиция как основа переиспользования

Popmotion построен вокруг идеи композиции. Любая трансформация может быть объединена с другой, создавая цепочки обработки значений.

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

import { pipe, clamp, blendColor } from "popmotion";

const transformValue = pipe(
  clamp(0, 100),
  v => v / 100,
  v => blendColor("#ff0000", "#0000ff", v)
);

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


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

В Popmotion часто требуется повторно использовать не сами анимации, а функции преобразования данных. Особенно это важно при работе с жестами, скроллом и физическими моделями.

const normalize = (min, max) => value =>
  (value - min) / (max - min);

const scale = factor => value => value * factor;

const transform = pipe(
  normalize(0, 500),
  scale(100)
);

Эти функции становятся строительными блоками для любых интерактивных сценариев.


Параметризация spring-анимаций

Физические модели в Popmotion, такие как spring, особенно хорошо поддаются переиспользованию через параметрические конфигурации.

import { animate } from "popmotion";

const createSpring = (stiffness = 180, damping = 12) => ({
  type: "spring",
  stiffness,
  damping
});

const slowSpring = createSpring(80, 30);
const fastSpring = createSpring(300, 15);

animate({
  from: 0,
  to: 100,
  ...slowSpring
});

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


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

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

const presets = {
  fade: {
    duration: 300,
    ease: t => t
  },
  slideUp: {
    duration: 500,
    ease: t => 1 - Math.pow(1 - t, 3)
  }
};

const animateFade = (element) =>
  animate({
    from: 0,
    to: 1,
    ...presets.fade,
    onUpdate: v => element.style.opacity = v
  });

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


Переиспользование через higher-order функции

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

const withOpacityAnimation = (animation) => (element) =>
  animation({
    onUpdate: v => {
      element.style.opacity = v;
    }
  });

const fadeAnimation = (options) =>
  animate({
    from: 0,
    to: 1,
    ...options
  });

const fadeElement = withOpacityAnimation(fadeAnimation);

fadeElement(document.querySelector(".box"));

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


Унификация анимационных потоков

Popmotion позволяет создавать унифицированные потоки значений, которые могут использоваться в разных сценариях: drag, scroll, physics.

import { pointer, animate } from "popmotion";

const bindDrag = (element) => {
  pointer({ x: 0, y: 0 })
    .start(v => {
      element.style.transform = `translate(${v.x}px, ${v.y}px)`;
    });
};

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

const applyTranslation = element => v => {
  element.style.transform = `translate(${v.x}px, ${v.y}px)`;
};

const bindDrag = (element) =>
  pointer({ x: 0, y: 0 }).start(applyTranslation(element));

Переиспользуемые easing-функции

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

const easeOutCubic = t => 1 - Math.pow(1 - t, 3);
const easeInOutQuad = t =>
  t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;

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

animate({
  from: 0,
  to: 1,
  duration: 500,
  ease: easeOutCubic
});

Комбинирование анимаций как способ переиспользования

Вместо создания сложных монолитных анимаций Popmotion позволяет собирать их из простых элементов.

import { tween, spring } from "popmotion";

const fadeIn = (element) =>
  tween({
    from: 0,
    to: 1,
    duration: 300,
    onUpdate: v => (element.style.opacity = v)
  });

const slideIn = (element) =>
  spring({
    from: -100,
    to: 0,
    stiffness: 200,
    damping: 20,
    onUpdate: v => (element.style.transform = `translateX(${v}px)`)
  });

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

const showElement = (element) => {
  fadeIn(element);
  slideIn(element);
};

Переиспользование достигается за счёт независимости каждой анимации.


Универсальные адаптеры для DOM

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

const setStyle = (element, property) => value => {
  element.style[property] = value;
};

const animateOpacity = (element) =>
  animate({
    from: 0,
    to: 1,
    duration: 400,
    onUpdate: setStyle(element, "opacity")
  });

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


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

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

const base = {
  duration: 300,
  ease: t => t
};

const theme = {
  duration: 500
};

const local = {
  from: 0,
  to: 1
};

const config = {
  ...base,
  ...theme,
  ...local
};

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


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

В Popmotion поток значений может быть отделён от его визуального представления. Это делает возможным повторное использование одного источника данных для разных эффектов.

import { pointer } from "popmotion";

const position = pointer({ x: 0, y: 0 });

const applyX = element => v => {
  element.style.left = v.x + "px";
};

const applyY = element => v => {
  element.style.top = v.y + "px";
};

const el1 = document.querySelector(".a");
const el2 = document.querySelector(".b");

position.start(applyX(el1));
position.start(applyY(el2));

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


Инкапсуляция поведения как ключ к масштабированию

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