2016-07-06 109 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而創建的。我用全套代碼更新了我的答案。 –

相關問題