Touch события

Popmotion использует унифицированную модель указателей (pointer model), которая объединяет мышь, касания и перо в единый поток событий. Это устраняет необходимость отдельно обрабатывать mousedown, touchstart, pointerdown и связанные с ними события низкого уровня.

Основной принцип — работа не с типом устройства ввода, а с абстрактным указателем, обладающим координатами, скоростью и фазой взаимодействия.


Базовая обработка касаний через pointer

Функция pointer создаёт поток координат, который реагирует на начало взаимодействия и отслеживает движение вплоть до завершения.

import { pointer } from 'popmotion';

const track = pointer({
  x: 0,
  y: 0
});

const subscription = track.start((v) => {
  console.log(v.x, v.y);
});

Каждое событие возвращает объект состояния указателя:

  • x, y — текущие координаты
  • deltaX, deltaY — смещение с предыдущего кадра
  • velocityX, velocityY — скорость движения
  • point — исходные координаты взаимодействия

Модель потоковая: при старте взаимодействия Popmotion сам подписывается на DOM-события и обновляет значения до момента завершения жеста.


Принцип жизненного цикла касания

Указатель проходит через несколько фаз:

  • start — фиксация начальной точки касания
  • move — непрерывное обновление координат
  • end — завершение взаимодействия
  • cancel — принудительное прерывание системой

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


Нормализация данных указателя

Одной из ключевых задач библиотеки является приведение входных данных к единому формату.

Для каждого кадра вычисляются:

  • абсолютная позиция относительно контейнера
  • относительное смещение
  • скорость с сглаживанием
  • направление движения
{
  x: 240,
  y: 120,
  deltaX: 4,
  deltaY: -2,
  velocityX: 0.32,
  velocityY: -0.18
}

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


Работа с жестами перетаскивания

Поверх pointer-модели строится поведение drag. Popmotion реализует его как композицию фильтров и ограничений.

import { pointer, listen, value } from 'popmotion';

const position = value({ x: 0, y: 0 });

pointer()
  .start((pointerValue) => {
    position.update(pointerValue);
  });

Перетаскивание фактически представляет собой непрерывное присваивание координат состояния.


Ограничение области движения

В сенсорных интерфейсах часто требуется ограничение перемещения объекта.

Popmotion использует трансформации данных потока:

  • clamp (ограничение диапазона)
  • interpolate (нормализация)
  • offset (смещение координат)
import { pointer, transform } from 'popmotion';

const { clamp } = transform;

const xClamp = clamp(0, 300);

pointer().start((v) => {
  const x = xClamp(v.x);
  console.log(x);
});

Ограничения применяются не к DOM, а к потоку данных, что делает систему декларативной.


Многоточечные касания

Для сенсорных экранов критична поддержка multitouch. Popmotion предоставляет механизм отслеживания нескольких указателей одновременно.

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

import { multitouch } from 'popmotion';

multitouch().start((points) => {
  points.forEach((p) => {
    console.log(p.x, p.y);
  });
});

Структура данных представляет массив активных точек:

[
  { id: 0, x: 120, y: 80 },
  { id: 1, x: 200, y: 140 }
]

Это позволяет реализовывать жесты масштабирования, вращения и сложных манипуляций.


Вычисление жестов pinch и rotate

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

Основные параметры:

  • расстояние между точками
  • угол между ними
  • центр жеста

Расстояние вычисляется через евклидову метрику:

genui{“math_block_widget_always_prefetch_v2”:{“content”:“d = ”}}

Изменение расстояния используется для масштабирования, а изменение угла — для поворота.


Сглаживание сенсорного ввода

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

Используется принцип экспоненциального сглаживания:

s_t = x_t + (1 - ) s_{t-1}

где:

  • x_t — текущее значение
  • s_t — сглаженное значение
  • α — коэффициент сглаживания

Это уменьшает резкие скачки координат при работе на сенсорных экранах.


Интеграция touch с анимациями

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

import { pointer, animate } from 'popmotion';

const x = pointer();

x.start((v) => {
  animate({
    from: v.x,
    to: v.x + v.velocityX * 20,
    duration: 100
  });
});

Движение пальца становится источником параметров для кинетической анимации.


Обработка завершения касания и инерция

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

Основная идея:

  • фиксируется последняя скорость
  • применяется затухание
  • движение продолжается до остановки

Математически это можно представить как экспоненциальное затухание скорости:

genui{“math_block_widget_always_prefetch_v2”:{“content”:“v(t) = v_0 e^{-kt}”}}

где скорость постепенно стремится к нулю.


Координация сенсорных потоков с DOM

Popmotion не привязывает сенсорные события напрямую к DOM-обработчикам. Вместо этого используется подписочная модель.

Это позволяет:

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

DOM становится лишь потребителем вычисленного состояния, а не источником логики.


Композиция жестов

Сложные взаимодействия строятся через композицию простых потоков:

  • pointer → drag
  • multitouch → pinch
  • pointer + transform → kinetic scroll

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

Так формируется цепочка:

input → normalization → gesture recognition → state update → animation

Разделение этапов позволяет масштабировать систему взаимодействия без усложнения базового кода.