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

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

Комбинирование физических моделей начинается с понимания, что каждая модель решает свою задачу:

  • inertia отвечает за продолжение движения после взаимодействия
  • spring моделирует упругие возвраты и колебания
  • decay обеспечивает затухание скорости
  • tween управляет детерминированными переходами
  • physics задаёт поведение через силы и ускорения

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

Пример базового разделения: перетаскивание элемента с последующим инерционным скроллом и финальным «пружинным» возвратом в границы.

import { pointer, inertia, spring, animate } from 'popmotion';

const position = { x: 0, y: 0 };

pointer()
  .start((v) => {
    position.x = v.x;
    position.y = v.y;
  });

// после отпускания жеста:
inertia({
  from: position.x,
  velocity: 500
}).start({
  update: v => position.x = v,
  complete: () => {
    spring({
      from: position.x,
      to: 300,
      stiffness: 200
    }).start(v => position.x = v);
  }
});

Здесь происходит явное разделение фаз: жест → инерция → пружина.

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

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

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

  • инерция вычисляет финальную позицию
  • пружина корректирует её до допустимой области
  • tween завершает визуальный переход
import { inertia, spring, tween } from 'popmotion';

function sequence(animations) {
  const run = (index, fromValue) => {
    const action = animations[index];
    if (!action) return;

    action(fromValue).start({
      complete: (v) => run(index + 1, v)
    });
  };

  run(0);
}

sequence([
  (from) => inertia({ from, velocity: 800 }),
  (from) => spring({ from, to: 200 }),
  (from) => tween({ from, to: 0, duration: 300 })
]);

Такой подход полезен при построении интерфейсов, где состояние проходит несколько трансформаций: «свободное движение → ограничение → фиксация».

Параллельные физические модели

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

Типичный пример — движение объекта, где:

  • позиция управляется инерцией
  • масштаб реагирует на скорость
  • вращение зависит от направления движения
import { inertia, spring } from 'popmotion';

const state = {
  x: 0,
  scale: 1,
  rotate: 0
};

inertia({
  from: 0,
  velocity: 600
}).start(v => {
  state.x = v;
  state.scale = 1 + Math.abs(v) / 2000;
});

spring({
  from: 0,
  to: 360,
  stiffness: 80
}).start(v => {
  state.rotate = v;
});

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

Смешивание сигналов движения

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

Например, инерция даёт базовое движение, а пружина корректирует его в сторону ограничений.

import { inertia, spring } from 'popmotion';

let base = 0;
let correction = 0;

inertia({
  from: 0,
  velocity: 1000
}).start(v => base = v);

spring({
  from: 0,
  to: 100,
  stiffness: 150
}).start(v => correction = v);

function render() {
  const result = base + correction * 0.2;
  requestAnimationFrame(render);
}

render();

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

Гибрид инерции и пружины

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

import { inertia, spring } from 'popmotion';

const bounds = { min: 0, max: 300 };

function applyBounds(v, velocity) {
  if (v < bounds.min || v > bounds.max) {
    spring({
      from: v,
      to: Math.min(bounds.max, Math.max(bounds.min, v)),
      stiffness: 300
    }).start();
  } else {
    inertia({
      from: v,
      velocity
    }).start();
  }
}

Ключевой момент — решение о переключении модели принимается не заранее, а в момент вычисления состояния.

Декомпозиция сложных движений

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

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

Каждый канал может использовать свою физическую модель.

import { inertia, spring } from 'popmotion';

const motion = {
  x: 0,
  y: 0,
  vx: 0,
  vy: 0
};

inertia({
  from: { x: 0, y: 0 },
  velocity: { x: 500, y: -200 }
}).start(v => {
  motion.x = v.x;
  motion.y = v.y;
  motion.vx = v.x * 0.1;
  motion.vy = v.y * 0.1;
});

spring({
  from: 0,
  to: 1,
  stiffness: 120
}).start(v => {
  motion.vx *= v;
  motion.vy *= v;
});

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

Управление конфликтами моделей

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

Подходы:

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

Пример с приоритетом:

let active = 'gesture';

function setPosition(v) {
  if (active === 'gesture') {
    position = v;
  }
}

function releaseGesture(velocity) {
  active = 'physics';

  inertia({
    from: position,
    velocity
  }).start(v => position = v);
}

Использование transform-пайплайнов

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

Типичный пайплайн:

  1. физическая модель генерирует сырое значение
  2. трансформ преобразует диапазон
  3. итоговое значение применяется к UI
import { inertia } from 'popmotion';
import { pipe, clamp, mapRange } from 'popmotion/transform';

const transform = pipe(
  clamp(0, 1000),
  mapRange(0, 1000, 0, 1)
);

inertia({
  from: 0,
  velocity: 800
}).start(v => {
  const normalized = transform(v);
  element.style.opacity = normalized;
});

Такое разделение позволяет комбинировать физику и визуальную логику без взаимного загрязнения.

Многослойная физика в интерфейсах жестов

В интерфейсах drag-and-drop часто используется несколько слоёв:

  • слой жеста (pointer)
  • слой инерции (inertia)
  • слой коррекции (spring)
  • слой ограничения (bounds)

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

import { pointer, inertia, spring } from 'popmotion';

let x = 0;
let velocity = 0;

pointer().start(v => {
  x = v.x;
});

function release(v) {
  inertia({
    from: x,
    velocity: v
  }).start(val => {
    x = val;

    if (val > 300) {
      spring({
        from: val,
        to: 300,
        stiffness: 250
      }).start(s => x = s);
    }
  });
}

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

Смешанные модели как единая система поведения

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

Каждая модель:

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

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