Изменяемые панели интерфейса

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


Инициализация изменяемого элемента

Для того чтобы элемент стал изменяемым, необходимо подключить Interact.js и применить метод interact() к DOM-элементу:

interact('.panel')
  .resizable({
    edges: { left: true, right: true, bottom: true, top: true },
    modifiers: [
      interact.modifiers.restrictEdges({
        outer: 'parent'
      }),
      interact.modifiers.restrictSize({
        min: { width: 100, height: 100 },
        max: { width: 800, height: 600 }
      })
    ],
    inertia: true
  })
  .on('resizemove', event => {
    let 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);
  });

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

  • edges — определяет стороны, за которые можно изменять размер элемента. Можно разрешить или запретить растягивание по конкретным направлениям.
  • modifiers — набор ограничений. restrictEdges предотвращает выход элемента за пределы родителя, restrictSize задаёт минимальные и максимальные размеры.
  • inertia — добавляет эффект инерции, делая растягивание более естественным.

Комбинация перемещения и изменения размера

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

interact('.panel')
  .draggable({
    listeners: {
      move(event) {
        let target = event.target;
        let x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
        let 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);
      }
    },
    inertia: true
  })
  .resizable({
    edges: { left: true, right: true, bottom: true, top: true },
    modifiers: [
      interact.modifiers.restrictEdges({
        outer: 'parent'
      }),
      interact.modifiers.restrictSize({
        min: { width: 100, height: 100 },
        max: { width: 800, height: 600 }
      })
    ],
    inertia: true
  });

Особенности комбинации:

  • Перемещение и изменение размера работают независимо друг от друга.
  • Трансформация через transform: translate() сохраняет позицию панели при изменении размеров.
  • Использование data-x и data-y позволяет корректно отслеживать текущее положение панели без смещения при повторных действиях.

Ограничение панели внутри контейнера

Чтобы панель не выходила за пределы родительского контейнера, применяются модификаторы restrictEdges и restrictSize. Важная деталь: необходимо учитывать, что ограничение размера должно работать совместно с ограничением перемещения, иначе панель может «вываливаться» за границы при быстром изменении размеров.

modifiers: [
  interact.modifiers.restrictEdges({
    outer: 'parent',
    endOnly: true
  }),
  interact.modifiers.restrictSize({
    min: { width: 150, height: 150 },
    max: { width: 600, height: 400 }
  })
]
  • endOnly: true гарантирует, что ограничение будет применяться только после завершения действия, что предотвращает дергание панели во время растягивания.

События жизненного цикла изменения размеров

Interact.js предоставляет несколько событий, на которых можно выполнять дополнительные действия при изменении размера:

  • resizestart — начало изменения размеров. Можно инициализировать визуальные эффекты или блокировать другие элементы.
  • resizemove — моментальное изменение размеров. Обычно используется для обновления стилей и атрибутов.
  • resizeend — завершение действия. Полезно для сохранения состояния панели, отправки данных на сервер или обновления интерфейса.

Пример:

.on('resizestart', event => {
  event.target.classList.add('resizing');
})
.on('resizeend', event => {
  event.target.classList.remove('resizing');
  console.log('Новые размеры:', event.rect.width, event.rect.height);
});

Адаптивные панели и динамическая подгрузка контента

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

interact('.panel')
  .resizable({
    edges: { right: true, bottom: true },
    modifiers: [
      interact.modifiers.restrictSize({
        min: { width: 200, height: 100 },
        max: { width: 800, height: 600 }
      })
    ]
  })
  .on('resizemove', event => {
    let target = event.target;
    target.style.width = event.rect.width + 'px';
    target.style.height = event.rect.height + 'px';

    // Автоподстройка содержимого
    const content = target.querySelector('.content');
    if (content) {
      content.style.height = (event.rect.height - 20) + 'px';
    }
  });
  • Автоподстройка внутреннего блока .content обеспечивает гибкую компоновку и предотвращает переполнение текста или других элементов.
  • Можно комбинировать с flex или grid, чтобы панели масштабировались вместе с контентом.

Стилизация изменяемых панелей

Для удобства пользователей необходимо визуально обозначать области для растягивания. Обычно применяют псевдоэлементы или специальные «ручки»:

.panel {
  position: absolute;
  border: 2px solid #333;
  box-sizing: border-box;
}

.panel::after {
  content: '';
  position: absolute;
  width: 12px;
  height: 12px;
  right: 0;
  bottom: 0;
  cursor: se-resize;
  background: #666;
}
  • cursor: se-resize информирует о направлении изменения размера.
  • Псевдоэлементы позволяют минимально нагружать HTML, сохраняя чистоту структуры.

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

  • Всегда использовать transform: translate вместо изменения top/left для плавного перемещения и оптимизации производительности.
  • Совмещать ограничения размеров и границ контейнера для предсказуемого поведения.
  • Использовать события жизненного цикла (resizestart, resizemove, resizeend) для синхронизации данных и визуальных эффектов.
  • При динамическом контенте обязательно учитывать внутренние отступы и размеры блоков, чтобы не нарушать компоновку интерфейса.