Demo | GitHub

Рис 1. Blazor Webassembly соединительные линии в SVG. Соединительные линии автоматически перестраиваются при изменении положения объектов.
Рис 1. Blazor Webassembly соединительные линии в SVG. Соединительные линии автоматически перестраиваются при изменении положения объектов.

В статье описан способ реализации соединительных линий между 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

Рис 2. Результат листинга 1. Точки соединены плавной линией.
Рис 2. Результат листинга 1. Точки соединены плавной линией.

Connector принимает на вход

  • координаты начальной и конечной точки,

  • направление входа в точку: сверху, справа, снизу, слева.

При этом, при динамическом изменении входных параметров, Connector перестраивает соединительную линию (см. рис. 1).

Если вам просто нужно готовое решение, статью читать не обязательно - скопируйте код компонента Connector из GitHub.

Основная идея

В SVG есть встроенная возможность рисовать плавные линии - элемент Path. Path поддерживает разные варианты рисования линий. Для соединительный линий, подходит реализованная в Path кубическая кривая Безье.

Что бы нарисовать кубическую кривую Безье нужно 4 точки (рис 3):

  • точка начала, точка конца,

  • опорная точка начала и опорная точка конца.

Рис 3. Кубическая кривая Безье нарисованная SVG элементом path. Синим отмечены координаты начальной и конечной точки, красным опорные точки.
Рис 3. Кубическая кривая Безье нарисованная SVG элементом path. Синим отмечены координаты начальной и конечной точки, красным опорные точки.

От положения опорных точке зависит вид кривой. На рис 4 первая опорная точка задрана вверх.

Рис 4. Кубическая кривая Безье нарисованная SVG элементом path. Первая опорная точка задрана вверх.
Рис 4. Кубическая кривая Безье нарисованная SVG элементом path. Первая опорная точка задрана вверх.

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. Расчет опорных точек для соединений слева и права. 70 - это произвольный коэффициент, подобран на глаз, никакого сакрального смысла нет,
Рис 5. Расчет опорных точек для соединений слева и права. 70 - это произвольный коэффициент, подобран на глаз, никакого сакрального смысла нет,

Если соединение “справа” (на рис 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)


  1. SWATOPLUS
    12.10.2021 07:57

    Идея и реализация замечательная. Однако не работает на Android Firefox с тачем. Наверняка нужно подписать на какие-то touch-события вдобавок к событиям мыши.


    1. Alex_BBB Автор
      12.10.2021 07:58
      +1

      Спасибо, да - нужно для тачскринов доделать.


  1. beh
    29.10.2021 11:18

    Почему то секунд 10 наблюдаю прелоадер, хотя по идее все должно быть очень быстро (если использовался blazor server).


    1. Alex_BBB Автор
      29.10.2021 11:25

      Да, не мгновенно загружается, у меня все же не 10 сек.

      "blazor server" тут ни при чем, все в браузере работает. Demo - это HTML страница с зависимостями (JS, wasm), на сервере статические файлы.


      1. beh
        29.10.2021 13:57

        Простите, комментарий был к другой статье. Я случайно перескочил на эту статью перед тем как написать комментарий :( А удалить не могу