skip to Main Content

Im currently trying center and angular component element in the center of an svg path element. but i can not find the way to make this possible. the idea is getting something like this:

enter image description here

the line connecting the boxes is the svg path, and + button is the element i want to position in the middle no matter how long is the line between the two boxes.

currently this is how the HTML looks like.

connection.component.html

<div class="relative">
  <svg data-testid="connection" #svg>
    <path [ngClass]="{ 'source-connected': !!data.source, 'target-connected': !!data.target }" [attr.d]="path" /> ----> path element
  </svg>
  <exb-node-menu #menu></exb-node-menu> ----> need to position in the center
</div>

here you can find a simple working example of the issue sandbox, in the folder custom-connection you can find the svg with button i need to place in the center of the svg

2

Answers


  1. I not sure if it’s possible to do this with path, but if you use lines it’s make things a lot simpler.

    The midpoint of the line. The midpoint MM between two points A(x1,y1)A(x1​,y1​) and B(x2,y2)B(x2​,y2​) is given by:

    enter image description here

    here is an example svg.

    here’s an example

    let selectedElement = null;
      let offsetX, offsetY;
    
      document.getElementById('startCircle').addEventListener('mousedown', startDrag);
      document.getElementById('endCircle').addEventListener('mousedown', startDrag);
      document.getElementById('svgCanvas').addEventListener('mousemove', drag);
      document.getElementById('svgCanvas').addEventListener('mouseup', endDrag);
      document.getElementById('svgCanvas').addEventListener('mouseleave', endDrag);
    
      function startDrag(evt) {
        selectedElement = evt.target;
        offsetX = evt.clientX - parseFloat(selectedElement.getAttribute('cx'));
        offsetY = evt.clientY - parseFloat(selectedElement.getAttribute('cy'));
      }
    
      function drag(evt) {
        if (selectedElement) {
            let coord = getMousePosition(evt);
            selectedElement.setAttribute('cx', coord.x);
            selectedElement.setAttribute('cy', coord.y);
            
            if (selectedElement.id === 'startCircle') {
                document.getElementById('lineElement').setAttribute('x1', coord.x);
                document.getElementById('lineElement').setAttribute('y1', coord.y);
            } else if (selectedElement.id === 'endCircle') {
                document.getElementById('lineElement').setAttribute('x2', coord.x);
                document.getElementById('lineElement').setAttribute('y2', coord.y);
            }
    
            // Update the center circle's position
            let x1 = parseFloat(document.getElementById('lineElement').getAttribute('x1'));
            let y1 = parseFloat(document.getElementById('lineElement').getAttribute('y1'));
            let x2 = parseFloat(document.getElementById('lineElement').getAttribute('x2'));
            let y2 = parseFloat(document.getElementById('lineElement').getAttribute('y2'));
    
            let midX = (x1 + x2) / 2;
            let midY = (y1 + y2) / 2;
    
            document.getElementById('centerCircle').setAttribute('cx', midX);
            document.getElementById('centerCircle').setAttribute('cy', midY);
        }
    }
    
      function endDrag(evt) {
        selectedElement = null;
      }
    
      function getMousePosition(evt) {
        let CTM = document.getElementById('svgCanvas').getScreenCTM();
        return {
          x: (evt.clientX - CTM.e + offsetX) / CTM.a,
          y: (evt.clientY - CTM.f + offsetY) / CTM.d
        };
      }
    <div>
      <svg height="310" width="550" id="svgCanvas">
        <!-- Line -->
        <line id="lineElement" x1="5" y1="5" x2="200" y2="200" style="stroke:rgb(255,0,0);stroke-width:2" />
        
        <!-- Circle at the start of the line -->
        <circle id="startCircle" cx="5" cy="5" r="5" style="fill:rgb(255,0,0)" />
        
        <!-- Circle at the end of the line -->
        <circle id="endCircle" cx="200" cy="200" r="5" style="fill:rgb(255,0,0)" />
        
        <!-- Circle at the mid of the line -->
        <circle id="centerCircle" cx="102.5" cy="102.5" r="5" style="fill:rgb(0,255,0)" />
      </svg>
    </div>

    ckeck out this solution https://codepen.io/Arun-David/pen/xxmyEjR

    Login or Signup to reply.
  2. I have this solution for u. You will have to adjust the button position because now it points to the top, left corner. I hope this helps u.

    import {
      Component,
      ElementRef,
      Input,
      OnChanges,
      ViewChild
    } from "@angular/core";
    import { ClassicPreset } from "rete";
    
    @Component({
      selector: "connection",
      template: `
        <div style="border:solid 1px red;">
          <svg #mySVG data-testid="connection" width="500" height="300">
            <defs>
              <path id="MyPath" [attr.d]="path" />
            </defs>
            <use xlink:href="#MyPath" fill="none" stroke="red" />
    
            <foreignObject xlink:href="#MyPath">
              <div style="border:1px green solid">I'm a div inside a SVG.</div>
            </foreignObject>
          </svg>
          <button
            [style.left.px]="cx"
            [style.top.px]="cy"
            style="position:absolute;"
          >
            Random button
          </button>
          <div></div>
        </div>
      `,
      styleUrls: ["./custom-connection.component.sass"]
    })
    export class CustomConnectionComponent implements OnChanges {
      @Input() data!: ClassicPreset.Connection<
        ClassicPreset.Node,
        ClassicPreset.Node
      >;
    
      public cx = 0;
      public cy = 0;
    
      @ViewChild("mySVG") mySVG!: ElementRef;
    
      _start: any;
      _end: any;
    
      @Input() set start(value: any) {
        this._start = value;
        this.ngOnChanges();
      }
      @Input() set end(value: any) {
        this._end = value;
        this.ngOnChanges();
      }
      @Input() path: string;
    
      ngOnChanges(): void {
        console.log("_start ", this._start);
        console.log("_end ", this._end);
        if (
          this._start === undefined ||
          this._start === null ||
          this._end === undefined ||
          this._end === null
        )
          return;
    
        let x = this._start.x - this._end.x;
        let y = this._start.y - this._end.y;
        this.cx =
          x < 0 ? Math.abs(x) / 2 + this._start.x : Math.abs(x) / 2 + this._end.x;
        this.cy =
          y < 0 ? Math.abs(y) / 2 + this._start.y : Math.abs(y) / 2 + this._end.y;
        console.log("go ", this.cx, " - ", this.cy);
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search