Физические модели в Popmotion редко используются изолированно. В реальных интерфейсах движение почти всегда представляет собой комбинацию нескольких принципов: инерции, упругости, затухания и дискретных переходов между состояниями. Сочетание этих моделей позволяет добиться поведения, которое воспринимается естественным и предсказуемым, особенно в сложных интерфейсах с жестами, перетаскиванием и анимацией компонентов.
Комбинирование физических моделей начинается с понимания, что каждая модель решает свою задачу:
Вместо попытки описать всё одной анимацией используется композиция: одна модель завершает свою фазу, другая подхватывает состояние и продолжает движение.
Пример базового разделения: перетаскивание элемента с последующим инерционным скроллом и финальным «пружинным» возвратом в границы.
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 это реализуется через ручное управление или композиционные функции.
Последовательность особенно полезна, когда каждая следующая модель зависит от результата предыдущей:
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);
}
Комбинирование физических моделей часто усиливается трансформациями значений. Popmotion предоставляет возможность строить цепочки преобразований, где физика является лишь первым этапом.
Типичный пайплайн:
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 часто используется несколько слоёв:
Каждый слой работает независимо, но влияет на общий результат.
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);
}
});
}
Такой подход позволяет комбинировать непрерывное движение и дискретные ограничения.
Наиболее устойчивые архитектуры используют не последовательное, а системное комбинирование. В этом случае физические модели рассматриваются как источники сил, а не как отдельные анимации.
Каждая модель:
Это позволяет строить сложные сцены, где одновременно работают несколько физических законов: инерция движения, упругость границ, затухание энергии и внешние воздействия.