Rotation - вращение элементов

Библиотека Interact.js предоставляет удобный способ реализации интерактивных действий с элементами DOM, включая перетаскивание, масштабирование и вращение. Вращение элементов позволяет пользователям изменять ориентацию объекта на странице с помощью мыши или сенсорного ввода, создавая динамичный и интерактивный интерфейс.

В Interact.js вращение реализуется через масштабирование с модификацией угла или через расширение стандартного draggable и gesturable API. Основные методы вращения опираются на события gesturemove или кастомные обработчики углов.


Настройка взаимодействия

Для начала необходимо подключить библиотеку:

<script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>

И выбрать элемент, который будет вращаться:

const rotateElement = document.querySelector('.rotatable');

Использование gesturable для вращения

Interact.js предоставляет метод gesturable(), который отслеживает жесты масштабирования и вращения на сенсорных устройствах. Пример базовой настройки вращения:

interact(rotateElement)
  .gesturable({
    onstart: function (event) {
      // Инициализация начального угла
      event.target.dataset.angle = event.target.dataset.angle || 0;
    },
    onmove: function (event) {
      let currentAngle = parseFloat(event.target.dataset.angle) || 0;
      currentAngle += event.da; // da — изменение угла в градусах
      event.target.style.transform = `rotate(${currentAngle}deg)`;
      event.target.dataset.angle = currentAngle;
    },
    onend: function (event) {
      // Можно сохранять финальный угол или выполнять другие действия
    }
  });

Ключевые моменты:

  • event.da — изменение угла за текущий шаг события, в градусах.
  • dataset.angle используется для хранения текущего угла между событиями, чтобы не терять позицию.
  • transform: rotate(...) применяется непосредственно к CSS элемента.

Комбинирование вращения с перетаскиванием и масштабированием

Для создания комплексного интерактивного элемента часто требуется совместить draggable, resizable и gesturable. Это позволяет пользователю не только вращать объект, но и перемещать и изменять его размеры.

interact(rotateElement)
  .draggable({
    listeners: {
      move(event) {
        const target = event.target;
        const x = (parseFloat(target.dataset.x) || 0) + event.dx;
        const y = (parseFloat(target.dataset.y) || 0) + event.dy;

        target.style.transform = `translate(${x}px, ${y}px) rotate(${target.dataset.angle || 0}deg)`;
        target.dataset.x = x;
        target.dataset.y = y;
      }
    }
  })
  .gesturable({
    onmove(event) {
      let angle = parseFloat(event.target.dataset.angle) || 0;
      angle += event.da;

      const x = parseFloat(event.target.dataset.x) || 0;
      const y = parseFloat(event.target.dataset.y) || 0;

      event.target.style.transform = `translate(${x}px, ${y}px) rotate(${angle}deg)`;
      event.target.dataset.angle = angle;
    }
  });

Особенности совместного использования:

  • Все трансформации нужно объединять в одном transform для предотвращения конфликтов CSS.
  • Сохраняются координаты перемещения (dataset.x, dataset.y) и угол (dataset.angle), чтобы корректно комбинировать действия.
  • Можно расширять комбинацию для resizable с учетом текущей ориентации.

Ограничение угла вращения

Interact.js позволяет устанавливать ограничения на угол вращения, чтобы элемент не превышал заданный диапазон:

interact(rotateElement)
  .gesturable({
    onmove(event) {
      let angle = parseFloat(event.target.dataset.angle) || 0;
      angle += event.da;

      // Ограничение от -45 до 45 градусов
      angle = Math.max(-45, Math.min(45, angle));

      const x = parseFloat(event.target.dataset.x) || 0;
      const y = parseFloat(event.target.dataset.y) || 0;

      event.target.style.transform = `translate(${x}px, ${y}px) rotate(${angle}deg)`;
      event.target.dataset.angle = angle;
    }
  });

Преимущества ограничения:

  • Предотвращает «вкручивание» элемента в неудобное положение.
  • Используется при интерфейсах с ограниченной ориентацией, например, вращение стрелки, циферблатов или панелей управления.

Вращение с помощью мыши на десктопе

Для устройств без сенсорного экрана вращение можно реализовать через mousedown + mousemove события с вычислением угла между курсором и центром элемента:

rotateElement.addEventListener('mousedown', startRotate);

function startRotate(event) {
  const rect = rotateElement.getBoundingClientRect();
  const centerX = rect.left + rect.width / 2;
  const centerY = rect.top + rect.height / 2;

  function onMouseMove(moveEvent) {
    const dx = moveEvent.clientX - centerX;
    const dy = moveEvent.clientY - centerY;
    let angle = Math.atan2(dy, dx) * (180 / Math.PI); // угол в градусах

    rotateElement.style.transform = `rotate(${angle}deg)`;
    rotateElement.dataset.angle = angle;
  }

  function stopRotate() {
    window.removeEventListener('mousemove', onMouseMove);
    window.removeEventListener('mouseup', stopRotate);
  }

  window.addEventListener('mousemove', onMouseMove);
  window.addEventListener('mouseup', stopRotate);
}

Особенности этого подхода:

  • Вычисление угла через atan2 обеспечивает корректное вращение во всех квадрантах.
  • Центр элемента выступает точкой вращения.
  • Можно легко интегрировать с draggable для комплексного взаимодействия.

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

  • Всегда использовать dataset для хранения текущих параметров трансформации. Это предотвращает потерю состояния при перерисовке страницы или пересоздании событий.
  • Комбинировать действия через единый transform, чтобы избежать конфликтов CSS.
  • При работе с мобильными устройствами предпочтительно использовать gesturable(), так как он автоматически учитывает мультитач и масштабирование.
  • Ограничение угла делает интерфейс более предсказуемым и управляемым, особенно для элементов с функциональными задачами.

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