Demo | GitHub

Рис 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. Точки соединены плавной линией.

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

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

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

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

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

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

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

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

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

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

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

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

Рис 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 к верхней синей точке соединительная линия подходит справа) , то координата опорной точки по 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.

Самореклама

Делаю быстрый бесплатный редактор блок-схем и интеллект карт https://dgrm.net/