Анимации переходов

Interact.js предоставляет мощные инструменты для создания интерактивных элементов с поддержкой drag-and-drop, resizable, gesturable событий. Анимации переходов позволяют не только визуально улучшить взаимодействие с элементами, но и создавать плавные реакции на действия пользователя, такие как перетаскивание или изменение размеров.

В основе анимаций лежит сочетание CSS-переходов и событий, которые Interact.js генерирует. Для создания плавного движения важно понимать разницу между динамическими позициями элемента и трансформациями через CSS.


Работа с трансформациями

Interact.js чаще всего использует CSS transform: translate(x, y) для перемещения элементов. Преимущество трансформаций:

  • Не вызывает reflow документа.
  • Обеспечивает плавность за счет аппаратного ускорения.
  • Легко комбинируется с CSS-переходами и анимациями.

Пример плавного перемещения при перетаскивании:

interact('.draggable')
  .draggable({
    listeners: {
      move(event) {
        const target = event.target;
        const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
        const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

        target.style.transform = `translate(${x}px, ${y}px)`;
        target.setAttribute('data-x', x);
        target.setAttribute('data-y', y);
      }
    }
  });

Чтобы добавить плавный переход после отпускания элемента, можно использовать CSS:

.draggable {
  transition: transform 0.3s ease-out;
}

И тогда при изменении позиции через style.transform элемент будет плавно перемещаться в новую точку.


Интеграция с Snap и Inertia

Interact.js имеет встроенные модули inertia и snap, которые создают эффект «отскока» и инерции:

  • inertia: true — элемент продолжает движение после отпускания, постепенно замедляясь.
  • snap — элемент автоматически привязывается к сетке или определенным точкам.

Пример с инерцией:

interact('.draggable')
  .draggable({
    inertia: true,
    modifiers: [
      interact.modifiers.snap({
        targets: [
          interact.snappers.grid({ x: 50, y: 50 })
        ],
        range: Infinity,
        relativePoints: [{ x: 0, y: 0 }]
      })
    ],
    listeners: {
      move(event) {
        const target = event.target;
        const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
        const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

        target.style.transform = `translate(${x}px, ${y}px)`;
        target.setAttribute('data-x', x);
        target.setAttribute('data-y', y);
      }
    }
  });

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


Настройка скорости и кривых анимации

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

.draggable {
  transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
  • ease-out — замедление в конце движения.
  • linear — постоянная скорость.
  • cubic-bezier — точная кастомизация кривой ускорения.

Можно динамически менять скорость через JS:

target.style.transition = 'transform 0.2s ease-in-out';

Это полезно, если нужно ускорить или замедлить анимацию в зависимости от состояния взаимодействия.


Анимации при изменении размеров

Interact.js поддерживает resizable элементы с возможностью плавного изменения размеров:

interact('.resizable')
  .resizable({
    edges: { left: true, right: true, bottom: true, top: true },
    listeners: {
      move(event) {
        const target = event.target;
        let x = (parseFloat(target.getAttribute('data-x')) || 0);
        let y = (parseFloat(target.getAttribute('data-y')) || 0);

        target.style.width  = `${event.rect.width}px`;
        target.style.height = `${event.rect.height}px`;

        x += event.deltaRect.left;
        y += event.deltaRect.top;

        target.style.transform = `translate(${x}px, ${y}px)`;
        target.setAttribute('data-x', x);
        target.setAttribute('data-y', y);
      }
    }
  });

Для плавного изменения размеров также добавляется CSS:

.resizable {
  transition: width 0.3s ease, height 0.3s ease, transform 0.3s ease;
}

Комбинирование drag и resize с анимациями

Плавность можно сохранить даже при одновременном перемещении и изменении размеров. Важный момент — использовать одинаковую transition для transform, width и height, чтобы избежать резких скачков.

.element {
  transition: transform 0.4s ease, width 0.4s ease, height 0.4s ease;
}

В JS-коде можно централизованно обновлять координаты и размеры:

function updateElement(event) {
  const target = event.target;
  let x = (parseFloat(target.getAttribute('data-x')) || 0) + (event.dx || 0);
  let y = (parseFloat(target.getAttribute('data-y')) || 0) + (event.dy || 0);

  if (event.rect) {
    target.style.width = `${event.rect.width}px`;
    target.style.height = `${event.rect.height}px`;
    x += event.deltaRect.left;
    y += event.deltaRect.top;
  }

  target.style.transform = `translate(${x}px, ${y}px)`;
  target.setAttribute('data-x', x);
  target.setAttribute('data-y', y);
}

Этот подход обеспечивает синхронную анимацию перемещения и изменения размеров.


Использование requestAnimationFrame для сложных анимаций

Для более сложных анимаций с многослойными эффектами или динамическим ускорением рекомендуется использовать requestAnimationFrame вместо CSS-переходов. Это позволяет контролировать каждый кадр движения и интегрировать дополнительные эффекты, такие как вращение, масштабирование или тени.

Пример анимации с инерцией:

let vx = 0, vy = 0;

interact('.draggable')
  .draggable({
    inertia: true,
    listeners: {
      move(event) {
        vx = event.dx;
        vy = event.dy;
        animate(event.target);
      }
    }
  });

function animate(target) {
  let x = parseFloat(target.getAttribute('data-x')) || 0;
  let y = parseFloat(target.getAttribute('data-y')) || 0;

  function frame() {
    x += vx;
    y += vy;
    target.style.transform = `translate(${x}px, ${y}px)`;

    vx *= 0.95;
    vy *= 0.95;

    target.setAttribute('data-x', x);
    target.setAttribute('data-y', y);

    if (Math.abs(vx) > 0.5 || Math.abs(vy) > 0.5) {
      requestAnimationFrame(frame);
    }
  }

  requestAnimationFrame(frame);
}

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


Важные рекомендации

  • Всегда использовать data-атрибуты для хранения координат вместо прямой работы с offsetLeft/Top. Это предотвращает конфликты с CSS-свойствами.
  • Объединять transform с width/height только при необходимости, иначе могут появляться визуальные «скачки».
  • Для высокой производительности на мобильных устройствах включать will-change: transform в CSS.
  • Использовать модификаторы Interact.js (snap, restrict, inertia) совместно с анимациями для создания интуитивного и плавного UX.