使用Fabric.js连接两个对象

6

我目前有一个画布应用程序,在其中可以添加对象(形状)。这是我的 FIDDLE。您基本上点击新模拟,它将初始化画布,然后您可以添加圆形或矩形。

我正在尝试添加一个名为“添加子项”的功能,您可以单击某个对象(形状),然后单击添加子项并添加另一个对象(形状),它们都与一条线连接。类似于这个 DEMO

我想到了这个功能的伪代码,大致如下:

function addChild(){

    get getActiveObject
    draw a line/arrow connect it with getActiveObject
    draw object connected with line
    should be able to move it / strecth it around


}

我想知道是否可能并如何开始。请给予建议。
2个回答

6
你需要监听 object:selected 事件来添加连接线,以及监听 object:moving 事件来更新线的坐标。
// function for drawing a line
function makeLine(coords) {
    return new fabric.Line(coords, {
        fill: 'red',
        stroke: 'red',
        strokeWidth: 5,
        selectable: false
    });
}

var canvas; 
window.newAnimation = function(){
   canvas = new fabric.Canvas('canvas');

   var selectedElement = null;
   canvas.on('object:selected', function(options) {
       // we are doing t oadd a connection
       if (canvas.connect) {
           canvas.connect = false;
           var from = selectedElement.getCenterPoint();
           var to = options.target.getCenterPoint();
           var line = makeLine([from.x, from.y, to.x, to.y]);
           canvas.add(line);

           // these take care of moving the line ends when the object moves
           selectedElement.moveLine = function() {
               var from = selectedElement.getCenterPoint();
               line.set({ 'x1': from.x, 'y1': from.y });
           };
           options.target.moveLine = function() {
               var to = options.target.getCenterPoint();
               line.set({ 'x2': to.x, 'y2': to.y });
           };
       }
       selectedElement = options.target;
   });

   canvas.on('object:moving', function(e) {
        e.target.moveLine();
        canvas.renderAll();
  });
}

window.addChild = function() {
   canvas.connect = true;
}

window.addChild会跟踪“添加子元素”是否被点击(并且应该添加到按钮的onclick事件中),以便下一个object:selected可以绘制线条(否则它只是跟踪当前选择的元素)。
<button onClick="addChild()">Add Child</button>

请注意,为了获得完整的解决方案,您需要能够取消添加子项,并且可能希望处理对象调整大小(目前在移动对象时该线会更新,但在调整对象大小时不会更新)。
示例代码 - http://jsfiddle.net/ctcdaxop/

这太棒了,为了取消添加子对象,我猜我只需要删除活动对象?例如,如果我选择一个子对象,它被视为活动对象,那么我应该简单地删除它吗? - user1010101
只需将 canvas.connect 设置为 false 即可。 - potatopeelings
http://jsfiddle.net/6zqbq7rp/ 这是我做的,但出于某些原因我无法删除连接线。我甚至尝试将canvas.connect设置为false。再次感谢您的帮助。 - user1010101
1
啊,好的,你需要存储一个指向行对象的引用,添加一个删除它的方法并在对象被删除时调用它。你还需要在移动处理程序中处理丢失的行。试一试,或者我稍后可以稍微调整一下。 - potatopeelings
没问题,谢谢。我会尽力完成这个任务。另外,当我试图将连接两个对象的代码分离到一个新函数中时,出现了一些问题。原因是我严格要求newAnimation函数创建一个新画布。所以如果你有机会,请告诉我是否可能。再次感谢您的出色贡献。 - user1010101
@user1010101 - 我添加了另一个答案(因为它与此答案不同,如果您想支持删除)。恐怕我们不能将所有代码从newAnimation中移出,因为我们需要附加处理程序,而为此您需要初始化的画布对象(仅在新动画处理程序中可用)。顺便说一下,点击新动画只会初始化一个新画布-您应该清除旧画布元素及其对象。 - potatopeelings

3

以下是您所需内容的新版本,包括对多连接和删除的支持。

添加子功能

function addChildLine(options) {
    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], {
        fill: 'red',
        stroke: 'red',
        strokeWidth: 5,
        selectable: false
    });
    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);
        });
    }

    // undefined instead of delete since we are anyway going to do this many times
    canvas.addChild = undefined;
}

function addChildMoveLine(event) {
    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();
    });
}

window.addChild = function () {
    canvas.addChild = {
        start: canvas.getActiveObject()
    }

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

由于我们在“添加动画”被点击之前没有画布参考,因此处理程序必须附加在“添加动画”处理程序中。

window.newAnimation = function () {
    canvas = new fabric.Canvas('canvas');

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

删除对象时同时删除相关连线

window.deleteObject = function () {
    var object = canvas.getActiveObject();

    // remove lines (if any)
    if (object.addChild) {
        if (object.addChild.from)
            // step backwards since we are deleting
            for (var i = object.addChild.from.length - 1; i >= 0; i--) {
                var line = object.addChild.from[i];
                line.addChildRemove();
                line.remove();
            }
        if (object.addChild.to)
            for (var i = object.addChild.to.length - 1; i >= 0; i--) {
                var line = object.addChild.to[i];
                line.addChildRemove();
                line.remove();
            }
    }

    // from original code
    object.remove();
}

Fiddle - http://jsfiddle.net/xvcyzh9p/


我应该将这个问题单独发布,以便您可以在那里回答吗?我已经点赞了,我觉得它应该得到更多的奖励。 - user1010101
没问题,就这样吧 - 点赞已经足够了。干杯! - potatopeelings
嘿,我刚刚发布了一个新问题,也许你可以帮忙看看。https://dev59.com/a4_ea4cB1Zd3GeqPMEj1 - user1010101
嗨,我想知道你有没有时间看一下我的问题:)。我正在查看Fabric JS动画教程@ http://fabricjs.com/animation-easing/. - user1010101

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