Интеграция с touch-событиями

Touch-события представляют собой механизм взаимодействия с сенсорными экранами и позволяют отслеживать касания, перемещения пальцев и их завершение. В отличие от классических событий мыши, они поддерживают множественные точки контакта, что делает возможным реализацию жестов: свайпов, пинчей, вращений.

Основные типы touch-событий:

  • touchstart — срабатывает при первом касании экрана
  • touchmove — вызывается при перемещении пальца по поверхности
  • touchend — фиксирует завершение касания
  • touchcancel — возникает при прерывании касания (например, системным событием)

Каждое событие содержит список касаний (TouchList), где можно получить координаты (clientX, clientY), идентификатор касания и другие параметры.


Связка anime.js и touch-событий

Библиотека anime.js не имеет встроенной абстракции для touch-жестов, однако отлично интегрируется с ними через стандартные обработчики событий. Основной подход — использование touch-событий для управления параметрами анимации в реальном времени.

Ключевая идея: touch-событие управляет состоянием анимации, а anime.js отвечает за визуальное представление.


Реакция на касание: запуск анимации

Простейший сценарий — запуск анимации при касании элемента.

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

element.addEventListener('touchstart', () => {
  anime({
    targets: element,
    scale: 1.2,
    duration: 300,
    easing: 'easeOutQuad'
  });
});

element.addEventListener('touchend', () => {
  anime({
    targets: element,
    scale: 1,
    duration: 300,
    easing: 'easeOutQuad'
  });
});

В этом примере создаётся эффект «нажатия», аналогичный кнопке.


Отслеживание движения: динамическое управление

Для более сложных взаимодействий используется событие touchmove. Оно позволяет изменять параметры анимации в зависимости от позиции пальца.

Пример: перемещение элемента за пальцем

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

element.addEventListener('touchmove', (e) => {
  const touch = e.touches[0];

  anime({
    targets: element,
    translateX: touch.clientX,
    translateY: touch.clientY,
    duration: 0
  });
});

Здесь анимация фактически используется как средство мгновенного обновления состояния.


Интерполяция движения

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

element.addEventListener('touchmove', (e) => {
  const touch = e.touches[0];

  anime({
    targets: element,
    translateX: touch.clientX,
    translateY: touch.clientY,
    duration: 200,
    easing: 'easeOutQuad'
  });
});

Это создаёт эффект инерции и плавности.


Реализация свайпов

Свайп определяется как быстрое перемещение пальца в определённом направлении. Для его реализации необходимо:

  1. Запомнить начальную точку (touchstart)
  2. Сравнить с конечной (touchend)
  3. Вычислить разницу
let startX = 0;

element.addEventListener('touchstart', (e) => {
  startX = e.touches[0].clientX;
});

element.addEventListener('touchend', (e) => {
  const endX = e.changedTouches[0].clientX;
  const diff = endX - startX;

  if (diff > 50) {
    anime({
      targets: element,
      translateX: 200,
      duration: 500
    });
  } else if (diff < -50) {
    anime({
      targets: element,
      translateX: -200,
      duration: 500
    });
  }
});

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


Жесты с инерцией

Для более естественного поведения используется скорость движения пальца. Это позволяет реализовать «бросок» объекта.

Вычисление скорости

let lastX = 0;
let lastTime = 0;

element.addEventListener('touchmove', (e) => {
  const touch = e.touches[0];
  const now = Date.now();

  const deltaX = touch.clientX - lastX;
  const deltaTime = now - lastTime;

  const velocity = deltaX / deltaTime;

  lastX = touch.clientX;
  lastTime = now;

  anime({
    targets: element,
    translateX: `+=${deltaX}`,
    duration: 0
  });

  element.dataset.velocity = velocity;
});

Использование скорости после отпускания

element.addEventListener('touchend', () => {
  const velocity = parseFloat(element.dataset.velocity) || 0;

  anime({
    targets: element,
    translateX: `+=${velocity * 200}`,
    duration: 800,
    easing: 'easeOutExpo'
  });
});

Так создаётся эффект продолжения движения после отпускания.


Множественные касания (multitouch)

Touch API поддерживает несколько точек касания. Это позволяет реализовать масштабирование и вращение.

Пример: масштабирование (pinch)

let initialDistance = null;

function getDistance(touches) {
  const dx = touches[0].clientX - touches[1].clientX;
  const dy = touches[0].clientY - touches[1].clientY;
  return Math.sqrt(dx * dx + dy * dy);
}

element.addEventListener('touchmove', (e) => {
  if (e.touches.length === 2) {
    const distance = getDistance(e.touches);

    if (!initialDistance) {
      initialDistance = distance;
    }

    const scale = distance / initialDistance;

    anime({
      targets: element,
      scale: scale,
      duration: 0
    });
  }
});

element.addEventListener('touchend', () => {
  initialDistance = null;
});

Предотвращение стандартного поведения

На мобильных устройствах браузер может перехватывать жесты (например, скролл). Для корректной работы анимаций используется preventDefault().

element.addEventListener('touchmove', (e) => {
  e.preventDefault();
}, { passive: false });

Важно указывать { passive: false }, иначе браузер игнорирует запрет.


Оптимизация производительности

Работа с touch-событиями требует высокой частоты обновлений, поэтому важно учитывать:

  • Использование duration: 0 для мгновенных обновлений
  • Минимизация количества создаваемых анимаций
  • Кэширование DOM-элементов
  • Ограничение вычислений внутри обработчиков

Эффективный подход — использовать одну анимацию и обновлять её параметры через .seek() или .pause() / .play().


Управление через timeline

anime.js позволяет объединять анимации в timeline, который можно контролировать через touch.

const timeline = anime.timeline({
  autoplay: false
})
.add({
  targets: element,
  translateX: 300,
  duration: 1000
});

element.addEventListener('touchmove', (e) => {
  const touch = e.touches[0];
  const progress = touch.clientX / window.innerWidth;

  timeline.seek(timeline.duration * progress);
});

Так реализуется «перемотка» анимации пальцем.


Архитектурные подходы

При построении сложных интерфейсов полезно разделять:

  • Логику ввода (touch-события)
  • Состояние (координаты, скорость, жест)
  • Отрисовку (anime.js)

Такое разделение упрощает поддержку и масштабирование.


Частые ошибки

  • Игнорирование touchcancel, что приводит к «зависшим» состояниям
  • Отсутствие очистки переменных после завершения жеста
  • Чрезмерное создание анимаций внутри touchmove
  • Использование анимаций с большой длительностью для realtime-взаимодействия

Комбинирование с pointer events

Современные браузеры поддерживают Pointer Events, которые объединяют мышь, touch и стилус. В некоторых случаях целесообразно использовать их вместо touch-событий, сохраняя совместимость.

Однако anime.js одинаково хорошо работает с любым типом событий, поскольку не зависит от источника ввода.


Практическое применение

Интеграция touch-событий с anime.js используется для:

  • drag-and-drop интерфейсов
  • мобильных каруселей
  • интерактивных карточек
  • игровых механик
  • кастомных скроллов
  • gesture-based UI

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