Переходы между состояниями

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

Любая анимация в Popmotion опирается на три компонента:

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

Переход между состояниями можно представить как функцию:

  • f(t) → значение на промежутке времени t

При этом t нормализуется в диапазон от 0 до 1 в случае временных анимаций или становится непрерывным потоком в физически моделируемых системах.

Классический переход:

import { tween } from "popmotion";

tween({
  from: 0,
  to: 100,
  duration: 600
}).start(v => {
  console.log(v);
});

Здесь происходит линейное или сглаженное преобразование состояния от 0 к 100.

Типы переходов между состояниями

Линейные переходы

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

Характеристика:

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

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

tween({
  from: { x: 0 },
  to: { x: 300 },
  duration: 400
}).start(({ x }) => {
  element.style.transform = `translateX(${x}px)`;
});

Плавные переходы через easing-функции

Реальные интерфейсы редко используют чистую линейность. Переходы смягчаются функциями easing, которые изменяют скорость изменения состояния.

Основные классы easing:

  • ускорение в начале
  • замедление в конце
  • комбинированные кривые

Пример:

import { tween, easing } from "popmotion";

tween({
  from: 0,
  to: 1,
  duration: 800,
  ease: easing.easeInOut
}).start(v => {
  element.style.opacity = v;
});

Easing трансформирует линейное время в нелинейную кривую, создавая ощущение инерции.

Физические переходы

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

Spring-переходы

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

  • жесткость
  • демпфирование
  • начальная скорость
import { spring } from "popmotion";

spring({
  from: 0,
  to: 500,
  stiffness: 120,
  damping: 20
}).start(v => {
  box.style.transform = `translateX(${v}px)`;
});

Поведение состояния становится зависимым не от времени, а от динамики системы.

Decay-переходы

Decay моделирует затухающее движение, где конечное состояние не задаётся явно.

import { decay } from "popmotion";

decay({
  from: 0,
  velocity: 500
}).start(v => {
  scrollContainer.scrollLeft = v;
});

Состояние постепенно теряет энергию, приближаясь к покою.

Переходы между сложными состояниями объектов

В реальных интерфейсах состояние редко представляет собой одно число. Обычно это объект с несколькими параметрами:

const stateA = {
  x: 0,
  y: 0,
  opacity: 0
};

const stateB = {
  x: 300,
  y: 200,
  opacity: 1
};

Popmotion интерполирует каждый ключ отдельно:

import { tween } from "popmotion";

tween({
  from: stateA,
  to: stateB,
  duration: 700
}).start(v => {
  box.style.transform = `translate(${v.x}px, ${v.y}px)`;
  box.style.opacity = v.opacity;
});

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

Промежуточные состояния и интерполяция

Механизм интерполяции отвечает за вычисление промежуточных значений между состояниями.

Для чисел используется линейная интерполяция:

  • v = a + (b - a) * t

Для объектов интерполяция выполняется по каждому полю отдельно.

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

Синхронизация нескольких переходов

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

Пример: одновременное изменение позиции и масштаба

import { tween } from "popmotion";

tween({
  from: { x: 0, scale: 1 },
  to: { x: 400, scale: 2 },
  duration: 500
}).start(v => {
  element.style.transform = `
    translateX(${v.x}px)
    scale(${v.scale})
  `;
});

Состояние остаётся единым, что исключает рассинхронизацию анимаций.

Управляемые переходы и контроль потока

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

const animation = tween({
  from: 0,
  to: 1000,
  duration: 2000
}).start(v => {
  slider.style.left = v + "px";
});

setTimeout(() => animation.stop(), 1000);

Состояние в этом случае фиксируется в момент остановки, и может быть использовано как новое начальное значение.

Переходы с изменением цели

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

let current = 0;

const controls = tween({
  from: current,
  to: 500,
  duration: 800
}).start(v => {
  current = v;
  box.style.transform = `translateX(${v}px)`;
});

// позже изменение цели
controls.stop();

tween({
  from: current,
  to: 100,
  duration: 400
}).start(v => {
  box.style.transform = `translateX(${v}px)`;
});

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

Комбинация временных и физических моделей

Сложные системы часто комбинируют tween и spring-переходы.

  • tween используется для предсказуемых UI-анимаций
  • spring — для интерактивных элементов
  • decay — для инерционных движений

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

Прерывание и переинициализация состояния

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

let x = 0;

const anim = tween({
  from: 0,
  to: 300,
  duration: 1000
}).start(v => {
  x = v;
});

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

Непрерывные переходы как поток состояний

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

  • текущее значение постоянно обновляется
  • новые переходы модифицируют поток
  • система не имеет «старт/стоп» в классическом смысле

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

Иерархия переходов

При проектировании интерфейсов переходы часто организуются в иерархии:

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

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

Каждый уровень управляет своим состоянием и передаёт изменения ниже по иерархии, формируя единый поток преобразований.