Адаптивная 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 для полностью резинового макета.