Адаптивная SVG-связь между элементами: пошаговая реализация

    При разработке интерфейсов часто требуется визуально соединить два блока линией, стрелкой или кривой. Если блоки позиционированы абсолютно в процентах, а макет должен быть резиновым, статическая картинка перестаёт работать при изменении размеров окна. Разберём, как построить адаптивную связь с помощью SVG, динамически рассчитывая координаты.

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

    Когда вы используете фоновое изображение или отдельный <img> для отрисовки соединительной линии, при изменении ширины или высоты экрана пропорции нарушаются. Элементы, расположенные через position: absolute с процентами, смещаются, а картинка остаётся фиксированного размера. В результате связь «рвётся» или накладывается не туда.

    Почему SVG - лучшее решение для адаптивной связи

    SVG (Scalable Vector Graphics) - это векторный формат, который масштабируется без потери качества. Вы можете разместить SVG-элемент поверх вашего макета (например, с position: absolute; width: 100%; height: 100%; pointer-events: none;) и рисовать линии, кривые Безье или стрелки между любыми точками. Координаты этих точек легко пересчитывать в процентах или пикселях относительно родительского контейнера.

    Расчёт координат для связи между элементами в процентах

    Допустим, у вас есть два блока A и B, каждый из которых позиционирован через left: X% и top: Y%. Чтобы нарисовать линию между ними, нужно знать их центры. Если блок имеет ширину W% и высоту H%, центр первого блока будет:

    • cx1 = left1 + W1/2 (в процентах)
    • cy1 = top1 + H1/2 (в процентах)

    Аналогично для второго блока. Эти значения можно передать в атрибуты SVG-линии <line> или в <path> для кривой. Чтобы линия правильно отображалась, контейнер SVG должен иметь ту же систему координат, что и родительский блок (обычно viewBox='0 0 100 100' при работе с процентами).

    Пример реализации: линия между двумя абсолютными блоками

    Рассмотрим простой HTML-код:

    <div style='position:relative; width:100%; height:500px;'>
      <div id='blockA' style='position:absolute; left:10%; top:20%; width:5%; height:5%; background:red;'></div>
      <div id='blockB' style='position:absolute; left:70%; top:60%; width:5%; height:5%; background:blue;'></div>
      <svg style='position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none;' viewBox='0 0 100 100'>
        <line x1='12.5%' y1='22.5%' x2='72.5%' y2='62.5%' stroke='black' stroke-width='0.5%' />
      </svg>
    </div>

    Здесь мы вручную вычислили центры: для блока A (10% + 2.5% = 12.5%, 20% + 2.5% = 22.5%), для блока B (70% + 2.5% = 72.5%, 60% + 2.5% = 62.5%). При изменении размеров окна линия будет перерисовываться браузером автоматически, так как SVG использует относительные единицы.

    Динамический расчёт с помощью JavaScript

    Если блоков много или их позиции меняются динамически, удобнее вычислять координаты скриптом. Получите getBoundingClientRect() для каждого блока и родительского контейнера, затем пересчитайте в проценты:

    function getCenterPercent(element, container) {
      const elemRect = element.getBoundingClientRect();
      const contRect = container.getBoundingClientRect();
      const cx = ((elemRect.left - contRect.left + elemRect.width / 2) / contRect.width) * 100;
      const cy = ((elemRect.top - contRect.top + elemRect.height / 2) / contRect.height) * 100;
      return { cx, cy };
    }

    После этого обновите атрибуты SVG-элементов (x1, y1, x2, y2 или d для path). Добавьте обработчик resize или используйте ResizeObserver для автоматического пересчёта.

    Рисуем кривую (дугу или кривую Безье)

    Чтобы связь выглядела более органично, вместо прямой линии используйте <path> с командами M (move), C (кубическая кривая Безье) или Q (квадратичная). Например, для плавной кривой между двумя точками:

    <path d='M 12.5 22.5 Q 42.5 42.5 72.5 62.5' stroke='black' fill='none' stroke-width='0.5%' />

    Контрольную точку (42.5, 42.5) можно рассчитать как среднее между началом и концом со смещением по вертикали или горизонтали для изгиба.

    Заключение

    Адаптивная связь между элементами на CSS-сетке - реальная задача, которая решается с помощью SVG и динамического расчёта координат. Откажитесь от статичных картинок в пользу векторной графики: это даст идеальное масштабирование, чёткость на любых экранах и возможность анимировать линии. Используйте проценты в viewBox SVG или пересчитывайте координаты через JavaScript для полностью резинового макета.

    Часто задаваемые вопросы