В Popmotion работа с указателем мыши строится вокруг абстракции
pointer tracking, которая унифицирует поведение mouse, touch и pen
input. Вместо прямого использования
addEventListener('mousemove') применяется реактивная
модель: поток координат преобразуется в значения, которые можно
связывать с анимациями, трансформациями и физическими моделями
движения.
В основе лежит функция pointer, которая создаёт поток
координат указателя относительно заданного элемента или viewport.
import { pointer } from 'popmotion';
const pointerTracker = pointer({
x: 0,
y: 0
});
pointerTracker.start((v) => {
console.log(v.x, v.y);
});
Каждое обновление возвращает объект состояния:
{
x: number,
y: number,
deltaX: number,
deltaY: number,
velocityX: number,
velocityY: number
}
Эти данные позволяют сразу работать не только с позицией, но и с динамикой движения.
Часто координаты нужно считать относительно конкретного элемента, а не окна браузера. Для этого передаётся DOM-узел:
const box = document.querySelector('.box');
const tracker = pointer({
element: box
});
tracker.start((v) => {
box.style.transform = `translate(${v.x}px, ${v.y}px)`;
});
Теперь координаты нормализованы относительно верхнего левого угла элемента.
Popmotion использует жизненный цикл подписки:
const tracker = pointer();
const subscription = tracker.start((v) => {
console.log(v);
});
subscription.stop();
Остановка подписки автоматически удаляет обработчики событий, предотвращая утечки памяти.
При отсутствии элемента координаты берутся относительно окна:
const tracker = pointer();
tracker.start(({ x, y }) => {
console.log('viewport:', x, y);
});
Такой режим удобен для глобальных эффектов: курсорные подсветки, фоновые анимации, параллакс.
Одной из ключевых особенностей Popmotion является встроенный расчёт скорости:
tracker.start(({ velocityX, velocityY }) => {
console.log(velocityX, velocityY);
});
Эти значения обновляются на основе разницы между кадрами, что позволяет строить физически правдоподобные анимации без ручных вычислений delta time.
Помимо абсолютных координат доступны приращения:
tracker.start(({ deltaX, deltaY }) => {
console.log(deltaX, deltaY);
});
Delta полезны при реализации drag-логики, где требуется смещение относительно предыдущего кадра.
На основе pointer tracking строится базовая модель перетаскивания:
let startX = 0;
let startY = 0;
const tracker = pointer();
tracker.start(({ x, y, isStart, isEnd }) => {
if (isStart) {
startX = x;
startY = y;
}
const dx = x - startX;
const dy = y - startY;
element.style.transform = `translate(${dx}px, ${dy}px)`;
});
Popmotion дополнительно может передавать флаги начала и конца взаимодействия, что позволяет управлять состояниями drag без внешних обработчиков событий.
Для ограничения области перемещения применяется комбинация с transform-функциями:
import { clamp } from 'popmotion';
const limitX = clamp(0, 300);
const limitY = clamp(0, 200);
tracker.start(({ x, y }) => {
const cx = limitX(x);
const cy = limitY(y);
element.style.transform = `translate(${cx}px, ${cy}px)`;
});
Это позволяет создавать жестко ограниченные области перетаскивания.
Для уменьшения резких скачков применяется интерполяция значений:
import { smooth } from 'popmotion';
const smoothX = smooth(0.2);
const smoothY = smooth(0.2);
tracker.start(({ x, y }) => {
const sx = smoothX(x);
const sy = smoothY(y);
element.style.transform = `translate(${sx}px, ${sy}px)`;
});
Сглаживание делает движение более инерционным и визуально стабильным.
Popmotion позволяет легко трансформировать входные значения:
import { transformMap } from 'popmotion';
const scaleX = transformMap(0, window.innerWidth, -1, 1);
const scaleY = transformMap(0, window.innerHeight, -1, 1);
tracker.start(({ x, y }) => {
const nx = scaleX(x);
const ny = scaleY(y);
console.log(nx, ny);
});
Такая нормализация используется для управления шейдерами, canvas-анимациями и 3D-сценами.
Pointer tracking часто используется вместе с физическими моделями движения:
import { spring } from 'popmotion';
let currentX = 0;
tracker.start(({ x }) => {
spring({
from: currentX,
to: x,
stiffness: 200,
damping: 20
}).start(v => {
currentX = v;
element.style.left = `${v}px`;
});
});
Такое сочетание создаёт эффект инерции и плавного следования за курсором.
Pointer tracking поддерживает мультитач и несколько устройств ввода:
const tracker = pointer();
tracker.start(({ point }) => {
console.log(point);
});
Каждая точка содержит отдельные координаты, что позволяет реализовать жесты масштабирования и вращения.
Чтобы избежать перегрузки render-цикла, данные часто привязываются к
requestAnimationFrame:
let latest = null;
tracker.start((v) => {
latest = v;
});
function render() {
if (latest) {
element.style.transform =
`translate(${latest.x}px, ${latest.y}px)`;
}
requestAnimationFrame(render);
}
render();
Такой подход отделяет поток событий от отрисовки.
Pointer tracking часто используется как входной поток в композиции Popmotion:
import { value, decay } from 'popmotion';
const x = value(0);
tracker.start(({ velocityX }) => {
decay({
from: x.get(),
velocity: velocityX
}).start(x);
});
Это позволяет создавать инерционные эффекты, основанные на реальной скорости движения курсора.
Pointer данные часто применяются для управления UI-слоями:
tracker.start(({ x, y }) => {
const rotateX = (y / window.innerHeight - 0.5) * 30;
const rotateY = (x / window.innerWidth - 0.5) * -30;
card.style.transform =
`rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
});
Такой подход создаёт эффект 3D-наклона элементов при движении курсора.
Для корректного управления ресурсами важно останавливать поток при уничтожении компонента:
const tracker = pointer();
const sub = tracker.start(handler);
// позже
sub.stop();
Это полностью удаляет внутренние обработчики и прекращает расчёты.
Pointer tracking в Popmotion становится базовым строительным блоком для:
Его реактивная модель позволяет связать ввод пользователя с анимацией без промежуточных императивных обработчиков событий.