Хранение состояния

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

Координаты и смещения

Для отслеживания позиции элемента обычно используют объект с координатами x и y:

let position = { x: 0, y: 0 };

При обработке события dragmove эти координаты обновляются на основе смещения, предоставляемого библиотекой:

interact('.draggable')
  .draggable({
    listeners: {
      move(event) {
        position.x += event.dx;
        position.y += event.dy;

        event.target.style.transform =
          `translate(${position.x}px, ${position.y}px)`;
      }
    }
  });

Здесь event.dx и event.dy — это смещение от предыдущего события, а объект position хранит накопленные значения, которые позволяют элементу сохранять своё положение при последующих перемещениях.

Масштабирование и хранение размеров

Для масштабируемых элементов необходимо хранить не только координаты, но и размеры:

let state = {
  x: 0,
  y: 0,
  width: 100,
  height: 100
};

При событии resizemove обновление может выглядеть следующим образом:

interact('.resizable')
  .resizable({
    edges: { left: true, right: true, bottom: true, top: true },
    listeners: {
      move(event) {
        state.width = event.rect.width;
        state.height = event.rect.height;
        state.x += event.deltaRect.left;
        state.y += event.deltaRect.top;

        Object.assign(event.target.style, {
          width: `${state.width}px`,
          height: `${state.height}px`,
          transform: `translate(${state.x}px, ${state.y}px)`
        });
      }
    }
  });

Ключевой момент — использование event.deltaRect для корректного вычисления смещений при изменении размеров. Это гарантирует точное позиционирование элемента независимо от направления изменения его границ.

Использование data-атрибутов для сохранения состояния

Для долгосрочного хранения состояния между сессиями можно применять HTML-атрибуты data-*. Например:

function saveState(element, state) {
  element.dataset.x = state.x;
  element.dataset.y = state.y;
  element.dataset.width = state.width;
  element.dataset.height = state.height;
}

function loadState(element) {
  return {
    x: parseFloat(element.dataset.x) || 0,
    y: parseFloat(element.dataset.y) || 0,
    width: parseFloat(element.dataset.width) || 100,
    height: parseFloat(element.dataset.height) || 100
  };
}

Это позволяет синхронизировать внутреннее состояние с DOM, что особенно удобно при динамически создаваемых элементах или при необходимости сериализации состояния для сохранения в локальное хранилище.

Интеграция с локальным хранилищем

Для постоянного хранения состояния на стороне клиента можно использовать localStorage:

function persistState(key, state) {
  localStorage.setItem(key, JSON.stringify(state));
}

function retrieveState(key) {
  const data = localStorage.getItem(key);
  return data ? JSON.parse(data) : null;
}

Это позволяет при перезагрузке страницы восстановить позиции и размеры всех элементов. В связке с Interact.js можно применять следующую схему:

const saved = retrieveState('draggableBox');
if (saved) {
  const box = document.querySelector('.draggable');
  Object.assign(box.style, {
    width: `${saved.width}px`,
    height: `${saved.height}px`,
    transform: `translate(${saved.x}px, ${saved.y}px)`
  });
  state = saved;
}

Объединение состояния для нескольких взаимодействий

Interact.js поддерживает одновременное использование различных действий (drag, resize, gesture). Для этого целесообразно объединять состояние в единый объект:

let elementState = {
  position: { x: 0, y: 0 },
  size: { width: 100, height: 100 },
  rotation: 0
};

Обновление состояния может выполняться в разных обработчиках:

  • dragmove — обновление position
  • resizemove — обновление size и корректировка position
  • gesturemove — обновление rotation

Такой подход обеспечивает централизованное управление состоянием, упрощает сохранение в localStorage и работу с анимациями.

Хранение состояния для нескольких элементов

Для множества интерактивных элементов удобно использовать коллекцию состояний, например, объект с ключами по id или массив объектов:

let states = {};

document.querySelectorAll('.draggable').forEach(el => {
  const id = el.id;
  states[id] = { x: 0, y: 0 };
});

При событии dragmove можно обновлять конкретное состояние:

move(event) {
  const id = event.target.id;
  states[id].x += event.dx;
  states[id].y += event.dy;
  event.target.style.transform =
    `translate(${states[id].x}px, ${states[id].y}px)`;
}

Синхронизация с анимациями и CSS-переходами

Хранение состояния в объекте позволяет легко интегрировать его с CSS-анимациями:

event.target.style.transition = 'transform 0.2s ease';
event.target.style.transform = `translate(${state.x}px, ${state.y}px)`;

Преимущество такого подхода — плавная анимация изменений без потери точности позиции и размеров элемента.

Обработка событий окончания взаимодействий

Для оптимизации производительности и правильного сохранения состояния удобно использовать события dragend, resizeend:

listeners: {
  end(event) {
    saveState(event.target, state);
    persistState('draggableBox', state);
  }
}

Это позволяет фиксировать финальное состояние элемента после завершения взаимодействия, минимизируя лишние записи в localStorage во время движения или изменения размеров.

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