Q:

Fabricjs function to join 2 objects with line

I have a canvas where I can click to place icons and link those icons with a line, but I can't manage to get the lines to follow when moving the icon objects. I tried the following, but I can't manage to lock the ends of the lines to the icon objects.
My effort so far: JSFIDDLE
canvas.on('mouse:move', function (obj) {
var line = canvas.getItemByName('line');
var objEl = canvas.getActiveObject();
var type = objEl.get('type');
var leftEl = objEl.left;
var topEl = objEl.top;

canvas.getObjects().forEach((obj) => {
    var lineX1 = line.get('x1');
    var lineY1 = line.get('y1');
    var lineX2 = line.get('x2');
    var lineY2 = line.get('y2');

    if (lineX1 == leftEl && lineY1 == topEl) {
        line.set({
            x1: leftEl,
            y1: topEl
        });
        canvas.renderAll();
    };
});

line.set('opacity', 1);
});

A:

You have to keep the reference when you create your object, look at my comments:
    (function() {
      var canvas = this.__canvas = new fabric.Canvas('c', { selection: false });
      fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';

      function makeCircle(left, top, line1, line2, line3, line4) {
        // Note: Circle has its own variable
        var c = new fabric.Circle({
          left: left,
          top: top,
          strokeWidth: 5,
          radius: 12,
          fill: '#fff',
          stroke: '#666'
        });
        // Note: properties added to the object
        c.hasControls = c.hasBorders = false;

        // Note: references the line that refers to this joint
        c.line1 = line1;
        c.line2 = line2;
        c.line3 = line3;
        c.line4 = line4;

        return c;
      }

      function makeLine(coords) {
        return new fabric.Line(coords, {
          fill: 'red',
          stroke: 'red',
          strokeWidth: 5,
          selectable: false,
          evented: false,
        });
      }
      // Create a line and assign it to a variable
      var line = makeLine([ 250, 125, 250, 175 ]),
          line2 = makeLine([ 250, 175, 250, 250 ]),
          line3 = makeLine([ 250, 250, 300, 350]),
          line4 = makeLine([ 250, 250, 200, 350]),
          line5 = makeLine([ 250, 175, 175, 225 ]),
          line6 = makeLine([ 250, 175, 325, 225 ]);

      canvas.add(line, line2, line3, line4, line5, line6);

      canvas.add(
        // Here we set the references to the lines
        makeCircle(line.get('x1'), line.get('y1'), null, line),
        makeCircle(line.get('x2'), line.get('y2'), line, line2, line5, line6),
        makeCircle(line2.get('x2'), line2.get('y2'), line2, line3, line4),
        makeCircle(line3.get('x2'), line3.get('y2'), line3),
        makeCircle(line4.get('x2'), line4.get('y2'), line4),
        makeCircle(line5.get('x2'), line5.get('y2'), line5),
        makeCircle(line6.get('x2'), line6.get('y2'), line6)
      );

      // Here, by reference, we move the line according to the moving object
      canvas.on('object:moving', function(e) {
        var p = e.target;
        p.line1 && p.line1.set({ 'x2': p.left, 'y2': p.top });
        p.line2 && p.line2.set({ 'x1': p.left, 'y1': p.top });
        p.line3 && p.line3.set({ 'x1': p.left, 'y1': p.top });
        p.line4 && p.line4.set({ 'x1': p.left, 'y1': p.top });
        canvas.renderAll();
      });
    })();

A:

With the help of this answer I was able to reference to any canvas object and lock the lines as I go.

var canvas = new fabric.Canvas('c');

fabric.Canvas.prototype.getItemByName = function(name) {
  var object = null,
      objects = this.getObjects();

  for (var i = 0, len = this.size(); i < len; i++) {
    if (objects[i].name && objects[i].name === name) {
      object = objects[i];
      break;
    }
  }

  return object;
};

var currentColor;
var defaultIcon = {
  width: 40,
  height: 30,
  originX: 'center',
  originY: 'center',
  hasControls: false,
};
var iconTriangle = new fabric.Triangle(defaultIcon);
setColor('green');
canvas.add(iconTriangle);
//disable icon & hide when hovering over existing icon
canvas.on('mouse:over', function(obj) {
  iconTriangle.set('opacity', 0);
  canvas.renderAll();
});
//restor icon & unhide
canvas.on('mouse:out', function(e) {
  if (document.getElementById("on").checked == true) {
    // if 'target' is null, means mouse is out of canvas
    if (e.target) {
      iconTriangle.set('opacity', 1);
    } else {
      iconTriangle.left = -100;
      iconTriangle.top = -100;
    }
    canvas.renderAll();
  };
});
//move pointer icon
canvas.on('mouse:move', function(obj) {
  iconTriangle.top = obj.e.y - 100;
  iconTriangle.left = obj.e.x - 10;
  canvas.renderAll();
});

//count each by type and place new icon
canvas.on('mouse:up', function(e) {
  if (e.target) {
    return
  }
  var red = getObjectsBy((obj) => obj.fill === 'red').length;
  var green = getObjectsBy((obj) => obj.fill === 'green').length;
  var yellow = getObjectsBy((obj) => obj.fill === 'yellow').length;
  document.getElementById("greentally").value = green;
  document.getElementById("yellowtally").value = yellow;
  document.getElementById("redtally").value = red;
  addIcon(e.e.x - 10, e.e.y - 100, currentColor);
});

function setColor(color) {
  currentColor = color;
  iconTriangle.setColor(currentColor);
  canvas.renderAll();
}

function getObjectsBy(fn) {
  return canvas.getObjects().filter(fn)
}

function addIcon(x, y, color) {
  var icon = new fabric.Triangle(defaultIcon);
  if (document.getElementById("icon").checked == true) {
    icon.setColor(color);
    icon.left = x;
    icon.top = y;
    canvas.add(icon);
  } else if (document.getElementById("link").checked == true) {
    iconTriangle.set('opacity', 0);
    icon = null;
  };
  canvas.renderAll();
}

//set icon type
$(".switch").click(function() {
  if (document.getElementById("green").checked == true) {
    setColor('green');
  } else if (document.getElementById("yellow").checked == true) {
    setColor('yellow');
  } else if (document.getElementById("red").checked == true) {
    setColor('red');
  }
});

document.getElementById("icon").checked = true;

//link icons with line
    $("input").change(function () {

        mode = "draw";

        canvas.on('mouse:up', function (o) {
            isDown = true;

            var icon = canvas.getItemByName('icon');

            if (document.getElementById("link").checked == true) {
                if (canvas.getActiveObjects().length == 1) {
                    mode = "draw";

                    var pointer = canvas.getActiveObject();
                    var points = [pointer.left, pointer.top, pointer.left, pointer.top];

                    if (mode == "draw") {
                        line1 = new fabric.Line(points, {
                            strokeWidth: 5,
                            fill: '#39ff14',
                            stroke: '#39ff14',
                            originX: 'center',
                            originY: 'center',
                            selectable: false,
                            hasControls: false,
                            hasBorders: false,
                            evented: false,
                            targetFindTolerance: true,
                            name: 'line1',
                        });
                        canvas.add(line1);
                        line1.sendToBack();
                    };
                    canvas.renderAll();
                } else {
                    return;
                };
            };

        });

        if (document.getElementById("link").checked == true) {
            canvas.on('mouse:move', function (o) {
                line1.set('opacity', 0.4);
                if (o.target !== null) {
                    var x2 = o.target.left;
                    var y2 = o.target.top;
                    if (mode == "draw") {
                        line1.set({
                            x2: x2,
                            y2: y2
                        });
                        canvas.renderAll();
                    }
                } else {
                    var pointer = canvas.getPointer(o.e);

                    if (mode == "draw") {
                        line1.set({
                            x2: pointer.x,
                            y2: pointer.y
                        });
                        canvas.renderAll();
                    }
                }
                line1.set('opacity', 1);
            });

            canvas.on('mouse:down', function (e) {
                if (document.getElementById("link").checked == true) {
                    canvas.selection = false;
                    var line1 = canvas.getItemByName('line1');
                    var objEl = e.target;
                    var type = objEl.get('type');
                    if (type !== 'text') {
                        canvas.remove(line1);
                    }
                };
            });
        };
        canvas.renderAll();
    });
    
        $("input").change(function () {
        if (document.getElementById("link").checked == true) {

            // we need this here because this is when the canvas gets initialized
    ['object:moving', 'object:scaling'].forEach(addChildMoveLine);

            function addChildLine(options) {
                if (document.getElementById("link").checked == true) {
                    canvas.off('object:selected', addChildLine);

                    // add the line
                    var fromObject = canvas.addChild.start;
                    var toObject = options.target;
                    var from = fromObject.getCenterPoint();
                    var to = toObject.getCenterPoint();
                    var line = new fabric.Line([from.x, from.y, to.x, to.y], {
                        strokeWidth: 5,
                        fill: '#39ff14',
                        stroke: '#39ff14',
                        originX: 'center',
                        originY: 'center',
                        selectable: false,
                        hasControls: false,
                        hasBorders: false,
                        evented: false,
                        targetFindTolerance: true,
                        name: 'line',
                    });
                    canvas.add(line);
                    // so that the line is behind the connected shapes
                    line.sendToBack();

                    // add a reference to the line to each object
                    fromObject.addChild = {
                        // this retains the existing arrays (if there were any)
                        from: (fromObject.addChild && fromObject.addChild.from) || [],
                        to: (fromObject.addChild && fromObject.addChild.to)
                    }
                    fromObject.addChild.from.push(line);
                    toObject.addChild = {
                        from: (toObject.addChild && toObject.addChild.from),
                        to: (toObject.addChild && toObject.addChild.to) || []
                    }
                    toObject.addChild.to.push(line);

                    // to remove line references when the line gets removed
                    line.addChildRemove = function () {
                        fromObject.addChild.from.forEach(function (e, i, arr) {
                            if (e === line)
                                arr.splice(i, 1);
                        });
                        toObject.addChild.to.forEach(function (e, i, arr) {
                            if (e === line)
                                arr.splice(i, 1);
                        });
                    }
                    canvas.discardActiveObject();
                };
            }

            function addChildMoveLine(event) {
                if (document.getElementById("link").checked == true) {
                    canvas.on(event, function (options) {
                        var object = options.target;
                        var objectCenter = object.getCenterPoint();
                        // udpate lines (if any)
                        if (object.addChild) {
                            if (object.addChild.from)
                                object.addChild.from.forEach(function (line) {
                                    line.set({
                                        'x1': objectCenter.x,
                                        'y1': objectCenter.y
                                    });
                                })
                            if (object.addChild.to)
                                object.addChild.to.forEach(function (line) {
                                    line.set({
                                        'x2': objectCenter.x,
                                        'y2': objectCenter.y
                                    });
                                })
                        }

                        canvas.renderAll();
                    });
                };
            }

            function addChild(o) {
                if (document.getElementById("link").checked == true) {
                    var line = canvas.getItemByName('line');
                    var objEl = canvas.getActiveObject();
                    var type = objEl.get('type');
                    canvas.addChild = {
                        start: canvas.getActiveObject()
                    }

                    // for when addChild is clicked twice
                    canvas.off('object:selected', addChildLine);
                    canvas.on('object:selected', addChildLine);
                };
            };

            canvas.on('mouse:down', function (e) {
                if (document.getElementById("link").checked == true) {
                    var line = canvas.getItemByName('line');
                    canvas.selection = false;
                    if (e.target !== null) {
                        addChild();
                    } else {
                        canvas.discardActiveObject();
                    }

                    canvas.on('mouse:over', function () {
                        canvas.discardActiveObject();
                    });

                    canvas.on('mouse:out', function () {
                        canvas.forEachObject(function (obj) {
                            if (obj.get('name') !== 'cursor') {
                                obj.set('opacity', 1);
                            }
                        });
                    });

                };
            });
        };
    });
canvas {
  border: 1px solid #ccc;
}

.tally {
  position: fixed;
  width: 50px;
  left: 255px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.4.0/fabric.js"></script>

<b>PLACE:</b>
<input class="switch" name="iconOn" type="radio" id="icon" />
<b>ICON</b>
<input class="switch" name="iconOn" type="radio" id="link" />
<b>LINK</b>
<br>

<input class="switch" checked name="iconType" type="radio" id="green" />Green
<input class="tally" disabled id="greentally" type="text" value="0">
<br>
<input class="switch" name="iconType" type="radio" id="yellow" /> Yellow
<input class="tally" disabled id="yellowtally" type="text" value="0">
<br>
<input class="switch" name="iconType" type="radio" id="red" /> red
<input class="tally" disabled id="redtally" type="text" value="0">
<canvas id="c" width="600" height="300"></canvas>

