Fabric.js - 将对象:已修改的事件同步到另一个客户端

时间:2016-07-06 14:18:52

标签: events canvas socket.io synchronization fabricjs

协作模式:

将更改从Client#1的画布传播到客户端#2的画布的最佳方法是什么?以下是我捕获事件并将事件发送到Socket.io的方法。

$scope.canvas.on('object:modified',function(e) { 
  Socket.whiteboardMessage({
    eventId:'object:modified',
    event:e.target.toJSON()
  });
});

在接收方,此代码可以很好地将对象添加到屏幕上,但我找不到有关如何选择更新<的文档/ strong>画布中的现有对象。

fabric.util.enlivenObjects([e.event], function(objects) {
  objects.forEach(function(o) {
    $scope.canvas.add(o);
  });
});

我确实看到对象有个人设置器和一个批量设置器,但我无法弄清楚如何根据事件数据选择现有对象。

理想情况下,流程将是:

  1. 使用目标对象数据接收事件。
  2. 选择画布中的现有对象。
  3. 执行批量更新。
  4. 刷新画布。
  5. 希望有Fabric.JS经验的人可以帮我解决这个问题。谢谢!

    更新的答案 - 感谢AJM!

    AJM在为每个新创建的元素建议一个唯一ID时是正确的。我还能够为所有新创建的绘图路径创建新ID。以下是它的工作原理:

    var t = new fabric.IText('Edit me...', { 
      left: $scope.width/2-100, 
      top: $scope.height/2-50 
    });
    t.set('id',randomHash());
    $scope.canvas.add(t);
    

    我还捕获了新创建的路径并添加了一个ID:

    $scope.canvas.on('path:created',function(e) {
      if (e.target.id === undefined) {
        e.target.set('id',randomHash());
      }
    });
    

    但是,我遇到了一个问题,我的ID在控制台日志中可见,但在执行object.toJSON()后它不存在。这是因为Fabric有自己的序列化方法,可以将数据减少到标准化的属性列表。要包含其他属性,我必须像下面这样序列化传输数据:

    $scope.canvas.on('object:modified',function(e) { 
      Socket.whiteboardMessage({
        object:e.target.toJSON(['id']) // includes "id" in output.
      })
    });
    

    现在每个对象都有一个唯一的ID来执行更新。在接收器的代码中,我添加了AJM的对象查找功能。我将此代码放在我的应用程序的“启动”部分中,因此它只运行一次(当然,在加载Fabric.js之后!)

    fabric.Canvas.prototype.getObjectById = function (id) {
        var objs = this.getObjects();
        for (var i = 0, len = objs.length; i < len; i++) {
            if (objs[i].id == id) {
                return objs[i];
            }
        }
        return 0;
    };
    

    现在,每当收到带有白板数据的新socket.io消息时,我都可以通过此行在画布中找到它:

    var obj = $scope.canvas.getObjectById(e.object.id);
    

    插入和删除很容易,但是为了更新,最后一段代码就是这样做的:

    obj.set(e.object); // Updates properties
    $scope.canvas.renderAll(); // Redraws canvas
    $scope.canvas.calcOffset(); // Updates offsets
    

    所有这些都要求我处理以下事件。路径一旦被创建就被视为对象。

    $scope.canvas.on('object:added',function(e) { });
    $scope.canvas.on('object:modified',function(e) { });
    $scope.canvas.on('object:moving',function(e) { });
    $scope.canvas.on('object:removed',function(e) { });
    $scope.canvas.on('path:created',function(e) { });
    

1 个答案:

答案 0 :(得分:2)

我做了类似的事情,涉及多个用户之间的单个共享画布,并遇到了这个问题。

为了解决这个问题,我为添加到画布的每个对象添加了唯一的ID(使用javascript UUID生成器)(在我的情况下,可能有很多用户一次在画布上工作,因此我需要避免碰撞;在你的情况下,更简单的东西可以工作)。

Fabric对象'set方法将允许您添加任意属性,如id:o.set('id', yourid)。在向布局add()新的Fabric对象(并通过线路发送)之前,请添加ID属性。现在,您将拥有一个可以选择单个对象的唯一键。

从那里,您需要一种方法来按ID检索对象。这是我使用的:

fabric.Canvas.prototype.getObjectById = function (id) {
    var objs = this.getObjects();
    for (var i = 0, len = objs.length; i < len; i++) {
        if (objs[i].id == id) {
            return objs[i];
        }
    }

    return null;
};

当您从套接字接收数据时,请通过ID从画布中获取该对象,并使用适当的set方法或批量复制属性进行变更(或者,如果getObjectById返回null ,创造它。)