Отслеживание mouse событий

В Popmotion работа с указателем мыши строится вокруг абстракции pointer tracking, которая унифицирует поведение mouse, touch и pen input. Вместо прямого использования addEventListener('mousemove') применяется реактивная модель: поток координат преобразуется в значения, которые можно связывать с анимациями, трансформациями и физическими моделями движения.

Базовая модель pointer tracking

В основе лежит функция 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-элементу

Часто координаты нужно считать относительно конкретного элемента, а не окна браузера. Для этого передаётся DOM-узел:

const box = document.querySelector('.box');

const tracker = pointer({
  element: box
});

tracker.start((v) => {
  box.style.transform = `translate(${v.x}px, ${v.y}px)`;
});

Теперь координаты нормализованы относительно верхнего левого угла элемента.

Обработка событий мыши через start/stop модель

Popmotion использует жизненный цикл подписки:

const tracker = pointer();

const subscription = tracker.start((v) => {
  console.log(v);
});

subscription.stop();

Остановка подписки автоматически удаляет обработчики событий, предотвращая утечки памяти.

Работа с viewport координатами

При отсутствии элемента координаты берутся относительно окна:

const tracker = pointer();

tracker.start(({ x, y }) => {
  console.log('viewport:', x, y);
});

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

Извлечение скорости движения

Одной из ключевых особенностей Popmotion является встроенный расчёт скорости:

tracker.start(({ velocityX, velocityY }) => {
  console.log(velocityX, velocityY);
});

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

Использование delta значений

Помимо абсолютных координат доступны приращения:

tracker.start(({ deltaX, deltaY }) => {
  console.log(deltaX, deltaY);
});

Delta полезны при реализации drag-логики, где требуется смещение относительно предыдущего кадра.

Реализация 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 без внешних обработчиков событий.

Ограничение движения (clamp)

Для ограничения области перемещения применяется комбинация с 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-сценами.

Интеграция с spring-анимацией

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

Pointer tracking в Popmotion становится базовым строительным блоком для:

  • drag & drop интерфейсов
  • gesture recognition
  • parallax эффектов
  • physics-based animations
  • interactive canvas систем
  • UI реакций на движение курсора

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