使用Fabric.js函数将两个对象通过线连接起来

4
我有一个画布,可以在上面点击放置图标,并使用线条将这些图标连接起来,但是我无法在移动图标对象时使线条跟随移动。我尝试了以下方法,但我无法锁定线条的端点到图标对象上。
迄今为止我的努力: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);
});

enter image description here


http://fabricjs.com/stickman - Durga
谢谢您的评论,但是我在$(document).ready之后创建对象时没有成功实现stickman。 - Mark
2个回答

2

在创建对象时,需要保留引用,请查看我的注释:

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

谢谢您的评论,但是我无法将stickman演示应用于我的对象,因为它们都是在画布初始化之后添加的。 - Mark
无论何时添加它们,重要的是在这些对象内部添加引用线。 - Mosè Raguzzini

0

这个答案的帮助下,我能够引用任何画布对象并随时锁定线条。

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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接