[Fixed] How to Connect Two divs with lines in Angular

Issue

* Project Description

I am creating a responsive tournament Brackets tree web Page.

* Problem

My Problem is that I want to connect each Bracket "each bracket is a div of its own" to the next one with a decorated line, in general I want two connect two divs with a line that I can decorate.

– Example

Example1

Example1

Example2

Example2

* what I tried

I tried using the CanvasRenderingContext2D but I didn’t have much luck in it, or at least didn’t know how to fully utilize it

* What I want/ What’s expected

I want to be able to connect the two brackets/divs with a line that can be bent and moved "by the developer not the user" to design it.

  • p.s: This only needs to be shown on screens larger than "xs" so mobile devices is not a concern.

* My Code

Stackblitz Project

– Used Libraries and versions

  • Angular V:5.1
  • Flex-Layout "angular" V: 2.00 beta 12
  • Angular Material V: 5.01

Solution

<div style="position:absolute; left:0; top:0; width: 100px; height: 50px; background:red;"></div>
<div style="position:absolute; left:250px; top:150px; width: 100px; height: 50px; background: blue;"></div>

<svg style="position:absolute; left:0; top:0;" width="300" height="300">
    <line x1="100" y1="50" x2="250" y2="150" stroke="black"/>
</svg>

Example:

In app.component.html file:

<div class="cont">
  <div *ngFor="let item of arr_item; index as i" class="block d-flex align-items-center justify-content-between">
    <div class="d-flex flex-column" #left>
      <div *ngFor="let val of item.left" class="box box-left">
        {{val}}
      </div>
    </div>
    <div class="d-flex flex-column align-items-end" #right>
      <div *ngFor="let val of item.right" class="box box-right">
        {{val}}
      </div>
    </div>
  </div>
  <svg style="position: absolute; z-index: -1; width:100%; height:100%; top: 0; left: 0;">
    <polyline *ngFor="let item of polylines" 
      [attr.points]="item.points"
      [attr.fill]="item.fill"
      [attr.stroke]="item.stroke"
      [attr.stroke-width]="item.strokeWidth"
    />
  </svg>
</div>

In app.component.css file:

.cont {
  position: relative;
}
.box {
  padding: 10px;
  border: 1px solid #292929;
  width: fit-content;
  margin: 10px;
  max-width: 200px;
}
.block {
  margin: 10px;
}

In app.component.ts file:

import {
  AfterViewInit,
  Component,
  ElementRef,
  OnInit,
  QueryList,
  ViewChildren
} from "@angular/core";

export class Polyline {
  points: string;
  fill: string;
  stroke: string;
  strokeWidth: number;
  constructor(
    points?: string,
    fill?: string,
    stroke?: string,
    strokeWidth?: number
  ) {
    this.points = points || "";
    this.fill = fill || "none";
    this.stroke = stroke || "blue";
    this.strokeWidth = strokeWidth || 3;
  }
}

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit, AfterViewInit {
  arr_item = [
    {
      left: ["Lorem Ipsum", "ABC"],
      right: ["Hello World", "Hello"]
    },
    {
      left: [
        "Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
        "Lorem Ipsum is simply dummy text"
      ],
      right: [""]
    },
    {
      left: [
        "",
        "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"
      ],
      right: ["Rainbow"]
    }
  ];

  @ViewChildren("left", { read: ElementRef }) left: QueryList<ElementRef>;
  @ViewChildren("right", { read: ElementRef }) right: QueryList<ElementRef>;

  polylines: Polyline[] = [];

  ngAfterViewInit() {
    this.drawLines();
  }

  ngOnInit(): void {
    window.addEventListener("resize", () => {
      this.drawLines();
    });
  }

  getPos_line(ele) {
    var className = ele.getAttribute("class");
    if (className.indexOf("left") !== -1) {
      return {
        x: Math.round(ele.offsetLeft + ele.offsetWidth),
        y: Math.round(ele.offsetTop + ele.offsetHeight / 2)
      };
    } else {
      return {
        x: Math.round(ele.offsetLeft),
        y: Math.round(ele.offsetTop + ele.offsetHeight / 2)
      };
    }
  }

  getMaxX_Left(arr: any) {
    if (arr.length >= 1) {
      var maxX = this.getPos_line(arr[0]).x;
      for (var i = 0; i < arr.length; i++) {
        var posLine = this.getPos_line(arr[i]);
        if (posLine.x > maxX) {
          maxX = posLine.x;
        }
      }
      return maxX;
    } else {
      return null;
    }
  }

  getMinX_Right(arr: any) {
    if (arr.length >= 1) {
      var minX = this.getPos_line(arr[0]).x;
      for (var i = 0; i < arr.length; i++) {
        var posLine = this.getPos_line(arr[i]);
        if (posLine.x < minX) {
          minX = posLine.x;
        }
      }
      return minX;
    } else {
      return null;
    }
  }

  getCenY(left, right) {
    if (left.offsetHeight >= right.offsetHeight) {
      return Math.round(left.offsetTop + left.offsetHeight / 2);
    } else {
      return Math.round(right.offsetTop + right.offsetHeight / 2);
    }
  }

  drawLines() {
    this.polylines = [];
    for (var i = 0; i < this.arr_item.length; i++) {
      var polylines_temp: Polyline[] = [];
      var left_children = this.left.get(i).nativeElement.children;
      var right_children = this.right.get(i).nativeElement.children;
      var left_childrenLen = left_children.length;
      var right_childrenLen = right_children.length;
      var maxX_left = this.getMaxX_Left(left_children);
      var minX_right = this.getMinX_Right(right_children);
      var cenY = this.getCenY(
        this.left.get(i).nativeElement,
        this.right.get(i).nativeElement
      );
      var cenX = Math.round((maxX_left + minX_right) / 2);

      var space = 0;
      if (left_childrenLen > 1 && right_childrenLen > 1) {
        space = 10;
      }

      for (var j = 0; j < left_childrenLen; j++) {
        var posLine = this.getPos_line(left_children[j]);
        polylines_temp.push(
          new Polyline(`
            ${posLine.x},${posLine.y} 
            ${cenX - space},${posLine.y} 
            ${cenX - space},${cenY} 
            ${cenX},${cenY} 
          `)
        );
      }
      for (var j = 0; j < right_childrenLen; j++) {
        var posLine = this.getPos_line(right_children[j]);
        polylines_temp.push(
          new Polyline(`
            ${cenX},${cenY} 
            ${cenX + space},${cenY} 
            ${cenX + space},${posLine.y}  
            ${posLine.x},${posLine.y} 
          `)
        );
      }
      this.polylines = this.polylines.concat(polylines_temp);
    }
  }
}

Link to Stackblitz

Image Result

Leave a Reply

(*) Required, Your email will not be published