Инерционное прокручивание строится на имитации поведения реального объекта, получившего импульс. После завершения взаимодействия с пользователем элемент продолжает движение, постепенно теряя скорость из-за условного трения среды.
Ключевые параметры модели:
В Popmotion подобное поведение реализуется через физически ориентированные анимации, где движение описывается не ключевыми кадрами, а непрерывной эволюцией состояния.
Основой momentum scrolling является корректное измерение скорости pointer-события. Popmotion предоставляет инструменты отслеживания координат и вычисления производной по времени.
import { pointer } from 'popmotion';
const tracker = pointer({
x: 0,
y: 0
});
tracker.start((v) => {
console.log(v.x, v.y);
});
Однако для инерции важно не само положение, а изменение позиции во времени. Popmotion внутри pointer уже рассчитывает velocity, что позволяет использовать его как источник импульса.
После завершения взаимодействия (pointer up) полученная скорость
передаётся в функцию затухания. В Popmotion для этого используется
decay.
import { pointer, decay, animate } from 'popmotion';
let startX = 0;
let currentX = 0;
const track = pointer({
x: startX
}).start((v) => {
currentX = v.x;
});
Когда пользователь отпускает элемент, pointer останавливается, а накопленная скорость используется для продолжения движения.
decay реализует экспоненциальное замедление движения.
Это ключевой механизм momentum scrolling.
const momentum = decay({
from: currentX,
velocity: track.x.velocity,
power: 0.8,
timeConstant: 350
}).start({
update: (v) => {
currentX = v;
}
});
Основные параметры:
Физически это приближение к экспоненциальному распаду скорости:
v(t) = v₀ * e^(-t / τ)
Momentum scrolling требует переключения между двумя режимами: активным перетаскиванием и инерционным движением.
import { pointer, decay } from 'popmotion';
let animation;
let position = 0;
const drag = pointer({ x: position }).start((v) => {
position = v.x;
if (animation) animation.stop();
});
При завершении жеста запускается decay:
const startMomentum = (velocity) => {
animation = decay({
from: position,
velocity,
timeConstant: 300
}).start((v) => {
position = v;
});
};
Критический момент — фиксирование скорости в момент
pointer up. Popmotion позволяет получить velocity из
трекинга, либо вычислить его вручную.
let lastVelocity = 0;
const drag = pointer({ x: 0 }).start({
update: (v) => {
position = v.x;
},
velocity: (v) => {
lastVelocity = v.x;
},
complete: () => {
startMomentum(lastVelocity);
}
});
Momentum scrolling почти всегда требует ограничения границ. Popmotion
предоставляет clamp через модификаторы или ручную обработку
значения.
const clamp = (min, max) => (v) =>
Math.max(min, Math.min(max, v));
Использование в decay:
decay({
from: position,
velocity: lastVelocity
}).start((v) => {
position = clamp(0, 1000)(v);
});
Без ограничения инерция будет уходить в бесконечность, что не соответствует поведению интерфейсов.
Более естественное поведение достигается добавлением пружинного возврата после выхода за пределы области.
import { spring } from 'popmotion';
const snapBack = (value, min, max) => {
if (value < min || value > max) {
return spring({
from: value,
to: Math.min(max, Math.max(min, value)),
stiffness: 200,
damping: 25
});
}
};
Так создаётся эффект мягкого возврата вместо резкого обрезания движения.
Реальные интерфейсы используют комбинацию:
Сценарий:
import { pointer, decay, spring } from 'popmotion';
let position = 0;
let animation;
const min = 0;
const max = 800;
const update = (v) => {
position = v;
console.log(position);
};
const startDecay = (velocity) => {
animation = decay({
from: position,
velocity,
timeConstant: 400
}).start({
update: (v) => update(v),
complete: () => {
if (position < min || position > max) {
spring({
from: position,
to: position < min ? min : max,
stiffness: 180,
damping: 20
}).start(update);
}
}
});
};
pointer({ x: 0 }).start({
update: (v) => {
position = v.x;
if (animation) animation.stop();
update(position);
},
complete: (v) => {
startDecay(v.x.velocity);
}
});
Ощущение инерции регулируется несколькими параметрами:
timeConstant делает движение более
“тяжёлым”velocity снижает резкость стартаstiffness и damping влияет на
возвратДополнительное сглаживание:
const smoothVelocity = (v, prev) => prev * 0.8 + v * 0.2;
Momentum в интерфейсах зависит от длины и скорости жеста. Более длинный drag должен давать более сильную инерцию.
let history = [];
const record = (x) => {
const now = Date.now();
history.push({ x, now });
history = history.filter(h => now - h.now < 50);
};
Скорость рассчитывается по последним точкам, что повышает точность инерции.
Каждое новое взаимодействие должно мгновенно останавливать текущую анимацию:
if (animation) {
animation.stop();
}
Это предотвращает конфликт физической модели и пользовательского контроля, обеспечивая предсказуемое поведение системы.
Momentum scrolling в Popmotion часто используется для:
Поведение становится визуально близким к нативному скроллу благодаря сочетанию: