Fabricjs 函数用线连接 2 个对象

Fabricjs function to join 2 objects with line

我有一个 canvas,我可以在其中单击以放置图标,并且 link 那些带有线条的图标,但是在移动图标对象时我无法获得要遵循的线条。我尝试了以下方法,但无法将行尾锁定到图标对象。

我目前的努力: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);
});

创建对象时必须保留引用,看我的评论:

    (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();
      });
    })();

的帮助下,我能够引用任何 canvas 对象并随时锁定行。

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>