Restrict - ограничение движения

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

Метод restrict

Основной инструмент для ограничения — это modifiers, среди которых ключевой — restrict. Он применяется к объекту при конфигурации draggable, resizable или gesturable элементов.

Пример базового использования для ограничения перемещения в пределах родительского контейнера:

interact('.draggable')
  .draggable({
    modifiers: [
      interact.modifiers.restrict({
        restriction: 'parent',
        endOnly: true
      })
    ],
    listeners: {
      move(event) {
        const target = event.target;
        target.style.transform = `translate(${event.pageX}px, ${event.pageY}px)`;
      }
    }
  });

Ключевые параметры restrict:

  • restriction — задаёт границы перемещения. Может принимать:

    • строку 'parent' — ограничение внутри родителя;
    • CSS-селектор — ограничение в пределах выбранного элемента;
    • объект с координатами { top, left, bottom, right } — точные границы в пикселях;
    • функция, возвращающая область, позволяя динамически изменять границы.
  • endOnly — булевый параметр. Если true, ограничение применяется только в момент окончания перетаскивания. Если false, ограничение действует во время движения.

  • elementRect — определяет, какая точка элемента контролируется ограничением. По умолчанию { top: 0, left: 0, bottom: 1, right: 1 } (все границы). Можно ограничить, например, центр элемента: { top: 0.5, left: 0.5, bottom: 0.5, right: 0.5 }.

Ограничение по конкретным координатам

Для задания фиксированных границ можно использовать объект с координатами:

interact('.draggable')
  .draggable({
    modifiers: [
      interact.modifiers.restrict({
        restriction: { top: 50, left: 50, bottom: 300, right: 400 },
        endOnly: false
      })
    ],
    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);
      }
    }
  });

Здесь элемент не сможет выйти за прямоугольник, заданный координатами { top, left, bottom, right }.

Ограничение по оси

Иногда требуется ограничить движение по одной оси:

interact('.draggable-x')
  .draggable({
    modifiers: [
      interact.modifiers.restrict({
        restriction: 'parent',
        endOnly: false
      })
    ],
    axis: 'x',
    listeners: {
      move(event) {
        const target = event.target;
        const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;

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

Параметр axis: ‘x’ ограничивает движение только горизонтально, что полезно для слайдеров, полос прокрутки или горизонтальных панелей.

Динамическое ограничение

Использование функции для restriction позволяет менять границы в реальном времени, например, в зависимости от размера окна или других элементов:

interact('.draggable')
  .draggable({
    modifiers: [
      interact.modifiers.restrict({
        restriction: (x, y, element) => {
          const parent = element.parentElement.getBoundingClientRect();
          return {
            top: parent.top,
            left: parent.left,
            bottom: parent.bottom - element.offsetHeight,
            right: parent.right - element.offsetWidth
          };
        },
        endOnly: false
      })
    ],
    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);
      }
    }
  });

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

Особенности работы с endOnly

  • endOnly: true — элемент может временно выйти за границы во время перетаскивания, но при отпускании курсора автоматически возвращается в допустимую область.
  • endOnly: false — элемент не выходит за пределы области вообще, движение ограничено на каждом шаге.

Ограничение для resizable

Помимо перетаскивания, restrict можно применять к изменению размеров элементов:

interact('.resizable')
  .resizable({
    edges: { left: true, right: true, bottom: true, top: true },
    modifiers: [
      interact.modifiers.restrictSize({
        min: { width: 100, height: 50 },
        max: { width: 500, height: 300 }
      })
    ],
    listeners: {
      move(event) {
        const target = event.target;
        target.style.width = `${event.rect.width}px`;
        target.style.height = `${event.rect.height}px`;
      }
    }
  });

Здесь используется restrictSize — специальный модификатор для ограничения размеров, который работает по тем же принципам, что и restrict для движения.

Взаимодействие с другими модификаторами

Interact.js позволяет комбинировать modifiers:

  • snap — привязка к сетке или линиям;
  • restrict — ограничение по границам;
  • aspectRatio — поддержка пропорций при изменении размеров.

Например, можно одновременно ограничивать элемент по области и привязывать его к сетке:

interact('.draggable')
  .draggable({
    modifiers: [
      interact.modifiers.snap({
        targets: [interact.createSnapGrid({ x: 50, y: 50 })],
        range: Infinity,
        relativePoints: [{ x: 0, y: 0 }]
      }),
      interact.modifiers.restrict({
        restriction: 'parent',
        endOnly: false
      })
    ]
  });

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

Практические советы

  • Использовать endOnly: false для интерфейсов, где недопустимо временное выход за границы.
  • Для сложных динамических интерфейсов предпочтительно задавать функцию в restriction, чтобы учитывать размеры других элементов или окна.
  • При комбинировании с snap порядок модификаторов важен: сначала ограничение границ, затем привязка к сетке, чтобы элемент не «вываливался» из разрешенной области.
  • Для изменения размеров лучше использовать restrictSize, а не обычный restrict.

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