В статье описан способ реализации соединительных линий между SVG объектами. Соединительные линии автоматически перестраиваются при изменении положения объектов. Попутно рассмотрен метод OnParametersSet.
Предыдущая статья "Blazor WebAssembly: Drag and Drop в SVG".
Что получится в итоге
В итоге получиться Blazor компонент Connector. Пример использования:
<svg xmlns="http://www.w3.org/2000/svg">
<circle cx="85" cy="100" r="15" fill="#04dcd2" stroke="#fff" />
<circle cx="315" cy="250" r="15" fill="#04dcd2" stroke="#fff" />
<Connector
X1=100 Y1=100
Dir1=Direction.Right
X2=300 Y2=250
Dir2=Direction.Left />
</svg>
Листинг 1. Использование компонента Connector
Connector принимает на вход
координаты начальной и конечной точки,
направление входа в точку: сверху, справа, снизу, слева.
При этом, при динамическом изменении входных параметров, Connector перестраивает соединительную линию (см. рис. 1).
Если вам просто нужно готовое решение, статью читать не обязательно - скопируйте код компонента Connector из GitHub.
Основная идея
В SVG есть встроенная возможность рисовать плавные линии - элемент Path. Path поддерживает разные варианты рисования линий. Для соединительный линий, подходит реализованная в Path кубическая кривая Безье.
Что бы нарисовать кубическую кривую Безье нужно 4 точки (рис 3):
точка начала, точка конца,
опорная точка начала и опорная точка конца.
От положения опорных точке зависит вид кривой. На рис 4 первая опорная точка задрана вверх.
Blazor компонент Connector
В листинге 1 показано использование компонента Connector. Connector отображается на странице как SVG элемент path.
<path d="M @X1 @Y1 C @c1x @c1y, @c2x @c2y, @X2 @Y2" />
@code {
[Parameter] public Direction Dir1 { get; set; } = Direction.Right;
[Parameter] public double X1 { get; set; }
[Parameter] public double Y1 { get; set; }
[Parameter] public Direction Dir2 { get; set; } = Direction.Left;
[Parameter] public double X2 { get; set; }
[Parameter] public double Y2 { get; set; }
// reference points
double c1x;
double c1y;
double c2x;
double c2y;
…
public enum Direction {
Top,
Right,
Bottom,
Left
}
…
Листинг 2. Компонент Connector.
При рисовании соединительных линий:
точка начала и конца (X1, Y1; X2, Y2) (на рис 3 отмечены синим) заданы,
опорные точки (c1x, c1y; c2x, c2y) (на рис 3 отмечены красным) нужно рассчитывать.
Пусть соединительная линия может подходить к точке только с 4-х сторон (см. enum Direction в листинге 2):
сверху,
справа,
снизу,
слева.
Рассчитаем опорные точки для соединений слева и справа.
Если соединение “справа” (на рис 5 к верхней синей точке соединительная линия подходит справа) , то координата опорной точки по Y равна Y1 (c1y = Y1). Координата по X зависит X1: c1x = X1 + 70. 70 - это произвольный коэффициент, подобранный на глаз.
По аналогии рассчитываются опорные точки для соединений сверху и снизу.
Код реализации алгоритма смотрите на GitHub.
Динамическое перестроение соединительной линии при обновлении параметров. Метод OnParametersSet
При изменении значений входных параметров Connector должен пересчитывать опорные точки c1x, c1y; c2x, c2y.
Сигналом (событием) к пересчету может служить вызов set-теров входных параметров - листинг 3.
...
@code {
[Parameter] public Direction Dir1 {
get { ... }
set { ... calc(); };
} = Direction.Right;
[Parameter] public double X1 {
get { ... }
set { ... calc(); };
}
[Parameter] public double Y1 {
get { ... }
set { ... calc(); };
}
...
Листинг 3. Компонент Connector. Пересчет опорных точек в set-рах. Не лучший вариант.
Это не лучший вариант. Допустим параметры Connector-ора изменяются все разом:
<button @onclick=Update>Update</button>
<svg xmlns="http://www.w3.org/2000/svg">
<Connector
X1=X1 Y1=Y1
Dir1=Dir1
X2=X2 Y2=Y2
Dir2=Dir2 />
</svg>
@code {
Direction Dir1;
double X1;
double Y1;
Direction Dir2;
double X2;
double Y2;
void Update() {
Dir1 = Direction.Left;
X1 = 100;
Y1 = 100;
Dir1 = Direction.Right;
X1 = 200;
Y1 = 200;
}
...
Листинг 4. Единовременное обновление входных параметров Connector
Для каждого входного параметра будет вызван set-тер, т.е. метод Calc будет вызван 6 раз.
Решить эту проблему можно с помощью OnParametersSet.
...
@code {
[Parameter] public Direction Dir1 { get; set; } = Direction.Right;
[Parameter] public double X1 { get; set; }
[Parameter] public double Y1 { get; set; }
...
protected override void OnParametersSet() {
calc();
}
...
Листинг 5. Компонент Connector. Расчет опорных точек в методе OnParametersSet
Для листинга 4 метод OnParametersSet будет вызван один раз.
OnParametersSet используется не только для таких случаев, подробнее в документации Avoid unnecessary rendering of component subtrees.
Connector готов. С помощью Connector и Draggable из предыдущей статьи можно сделать подводные растения, или светильники IKEA - рис 1.
Комментарии (5)
beh
29.10.2021 11:18Почему то секунд 10 наблюдаю прелоадер, хотя по идее все должно быть очень быстро (если использовался blazor server).
Alex_BBB Автор
29.10.2021 11:25Да, не мгновенно загружается, у меня все же не 10 сек.
"blazor server" тут ни при чем, все в браузере работает. Demo - это HTML страница с зависимостями (JS, wasm), на сервере статические файлы.
beh
29.10.2021 13:57Простите, комментарий был к другой статье. Я случайно перескочил на эту статью перед тем как написать комментарий :( А удалить не могу
SWATOPLUS
Идея и реализация замечательная. Однако не работает на Android Firefox с тачем. Наверняка нужно подписать на какие-то touch-события вдобавок к событиям мыши.
Alex_BBB Автор
Спасибо, да - нужно для тачскринов доделать.