2016-07-06 167 views
2

协作模式:Fabric.js - 同步对象:修改事件到另一个客户端

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

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

在接收端,此代码的工作出色添加对象到屏幕上,但我无法找到如何选择更新在画布上的现有对象的文档。

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

我没看到,对象有个人setter和一个大容量二传手,但我无法弄清楚如何选择基于事件数据的现有对象。

在理想情况下,该流程将是:

  1. 与目标对象数据接收事件。
  2. 选择画布中的现有对象。
  3. 执行批量更新。
  4. 刷新画布。

希望有Fabric.JS经验的人可以帮我弄清楚这一点。谢谢!

UPDATED ANSWER - Thanks 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; 
}; 

现在,每当一个新的套接字。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

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

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

结构对象的set方法将让你添加一个任意的属性,如一个ID:o.set('id', yourid)。在您的画布上添加一个新的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,创建)。

+0

有趣,谢谢你的提示。你是如何解决将id添加到自由绘制模式下生成的路径的?这听起来像你使用路径:添加并分配一个ID,如果它还没有一个......是否正确? –

+1

不幸的是,我们不需要在我们的应用程序中支持免费的绘图,所以我无法回答那个问题(我在应用程序的一侧停放了一个包含形状,箭头,图像和几个文本对象的“调色板”我创建的;我可以基本保证除自由绘制路径以外的任何东西)。冒着猜测,但只要路径具有唯一的ID属性(即在那里或您设置的),所有的*应该*工作相同(您可以通过ID选择它们,然后应用适当的突变)。 – ajm

+1

绘图的工作方式与对象完全相同,除了我必须侦听路径:为了设置ID而创建的。我用全套代码更新了我的答案。 –

相关问题