协作模式:
如何最好地将客户端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);
});
});
我看到对象有单独的设置器和一个批量设置器,但我无法根据事件数据选择现有对象。理想情况下,流程应该是:
- 接收具有目标对象数据的事件。
- 在画布中选择现有对象。
- 执行批量更新。
- 刷新画布。
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) { });