Компонентный подход

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

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

Ключевые свойства компонентного подхода:

  • Изолированность логики
  • Переиспользуемость
  • Композиция через функции
  • Явное управление состоянием

Базовые строительные блоки Popmotion

Popmotion предоставляет несколько примитивов, которые становятся «компонентами» анимационной системы:

Анимации (actions)

  • tween
  • spring
  • decay

Каждая из них представляет собой поток значений во времени.

Значения (value)

Хранят текущее состояние анимации и позволяют подписываться на изменения:

import { value } from "popmotion";

const x = value(0);
x.subscribe(v => console.log(v));
x.update(100);

Композиционные функции

  • parallel — одновременное выполнение
  • chain — последовательное выполнение
  • stagger — выполнение с задержкой

Инкапсуляция анимации в компонент

Компонент в Popmotion — это функция, которая возвращает готовую анимацию или управляет состоянием.

Пример: компонент движения

import { tween } from "popmotion";

const moveX = (from, to, duration = 300) => {
  return tween({
    from,
    to,
    duration
  });
};

Теперь moveX можно использовать в разных частях приложения.


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

Компонент может содержать собственное состояние через value.

import { value, tween } from "popmotion";

const animatedValue = () => {
  const val = value(0);

  const animation = tween({
    from: 0,
    to: 100
  }).start(val);

  return {
    value: val,
    stop: () => animation.stop()
  };
};

Такой компонент:

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

Композиция компонентов

Компоненты легко комбинируются друг с другом.

Параллельная композиция

import { parallel } from "popmotion";

const fadeAndMove = (element) => {
  return parallel(
    moveX(0, 300),
    tween({ from: 0, to: 1 })
  ).start({
    update: ([x, opacity]) => {
      element.style.transform = `translateX(${x}px)`;
      element.style.opacity = opacity;
    }
  });
};

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

import { chain } from "popmotion";

const sequence = chain(
  moveX(0, 100),
  moveX(100, 0)
);

Разделение ответственности

Каждый компонент должен решать одну задачу:

  • движение → moveX
  • прозрачность → fade
  • масштаб → scale

Пример

const fade = (from, to) =>
  tween({ from, to });

const scale = (from, to) =>
  tween({ from, to });

Комбинирование:

parallel(
  fade(0, 1),
  scale(0.5, 1)
);

Параметризация компонентов

Компоненты должны быть гибкими через параметры.

const springMove = ({ from, to, stiffness = 100 }) => {
  return spring({
    from,
    to,
    stiffness
  });
};

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

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

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

Компоненты можно переиспользовать в разных UI-элементах.

const buttonHover = (element) => {
  const scaleUp = tween({ from: 1, to: 1.1 });
  const scaleDown = tween({ from: 1.1, to: 1 });

  element.addEventListener("mouseenter", () => {
    scaleUp.start(v => {
      element.style.transform = `scale(${v})`;
    });
  });

  element.addEventListener("mouseleave", () => {
    scaleDown.start(v => {
      element.style.transform = `scale(${v})`;
    });
  });
};

Реактивные компоненты

Popmotion хорошо сочетается с реактивным подходом: значения могут зависеть друг от друга.

import { value } from "popmotion";

const x = value(0);
const y = value(0);

x.subscribe(v => {
  y.update(v * 2);
});

Создается зависимость:

  • изменение x автоматически влияет на y

Интеграция с DOM

Компоненты часто управляют DOM напрямую.

const createMover = (element) => {
  return moveX(0, 200).start(v => {
    element.style.transform = `translateX(${v}px)`;
  });
};

Для повышения гибкости:

  • передавать элемент как параметр
  • не хардкодить селекторы

Управление жизненным циклом

Каждый компонент должен уметь:

  • запускаться
  • останавливаться
  • пересоздаваться
const createAnimation = () => {
  let control;

  return {
    start() {
      control = moveX(0, 100).start(console.log);
    },
    stop() {
      control && control.stop();
    }
  };
};

Декларативный подход через композицию

Компоненты позволяют описывать анимацию декларативно:

const animation = parallel(
  moveX(0, 300),
  fade(0, 1)
);

Вместо пошагового управления:

  • описывается результат
  • Popmotion управляет выполнением

Расширяемость компонентов

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

const withLogging = (animationFactory) => (...args) => {
  const animation = animationFactory(...args);

  return {
    start: (update) => {
      return animation.start(v => {
        console.log(v);
        update(v);
      });
    }
  };
};

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

const loggedMove = withLogging(moveX);

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

На основе базовых компонентов строятся более сложные.

const modalAnimation = (element) => {
  return parallel(
    moveX(-100, 0),
    fade(0, 1)
  ).start(([x, opacity]) => {
    element.style.transform = `translateX(${x}px)`;
    element.style.opacity = opacity;
  });
};

Такой компонент:

  • скрывает детали реализации
  • предоставляет простой API

Управление зависимостями между компонентами

Компоненты могут запускать друг друга:

const first = moveX(0, 100);
const second = moveX(100, 200);

first.start({
  complete: () => second.start(console.log)
});

Или через chain:

chain(first, second);

Тестируемость

Из-за изоляции компоненты легко тестировать:

const anim = moveX(0, 100);

anim.start({
  update: v => {
    // проверка значений
  }
});

Можно:

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

Переход к архитектуре

Компонентный подход в Popmotion постепенно приводит к архитектуре, где:

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

Такая структура делает код:

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