Fabric.js – 如何使用自定义属性在服务器上保存canvas

我希望能够将当前canvas的状态保存到服务器端数据库,可能是一个JSONstring,然后用loadFromJSON恢复它。 通常情况下,这很容易实现:

 var canvas = new fabric.Canvas(); function saveCanvas() { // convert canvas to a json string var json = JSON.stringify( canvas.toJSON() ); // save via xhr $.post('/save', { json : json }, function(resp){ // do whatever ... }, 'json'); } 

接着

 function loadCanvas(json) { // parse the data into the canvas canvas.loadFromJSON(json); // re-render the canvas canvas.renderAll(); // optional canvas.calculateOffset(); } 

不过,我也使用内build的Object#set方法在我添加到canvas的结构对象上设置了一些自定义属性:

 // get some item from the canvas var item = canvas.item(0); // add misc properties item.set('wizard', 'gandalf'); item.set('hobbit', 'samwise'); // save current state saveCanvas(); 

问题是,当我检查服务器端的请求时,我看到我的自定义属性没有从canvasparsing,并与其他一切发送。 这可能与如何toObject方法删除不是对象类中的默认属性的东西有关。 解决这个问题的好方法是什么,这样我就能够从服务器发送的JSONstring中保存恢复canvas,并且恢复的canvas也将包含我的自定义属性? 谢谢。

好问题。

如果您将自定义属性添加到对象,那么这些对象在某些方面可能是“特殊的”。 看来子类化他们将是一个合理的解决scheme。

例如,以下是我们如何将一个fabric.Image子类化为一个命名的图像。 这些图像对象可以有“甘道夫”或“Samwise”这样的名字。

 fabric.NamedImage = fabric.util.createClass(fabric.Image, { type: 'named-image', initialize: function(element, options) { this.callSuper('initialize', element, options); options && this.set('name', options.name); }, toObject: function() { return fabric.util.object.extend(this.callSuper('toObject'), { name: this.name }); } }); 

首先,我们给这些对象一个types。 loadFromJSON使用此types自动调用fabric.<type>.fromObject方法。 在这种情况下,它将是fabric.NamedImage.fromObject

然后,我们重写initialize (构造函数)实例方法,也初始化一个对象(如果给定的属性)设置“名称”属性。

然后我们覆盖toObject实例方法,在返回的对象中包含“name”(这是fabric中对象序列化的基石)。

最后,我们还需要实现前面提到的fabric.NamedImage.fromObject ,以便loadFromJSON知道在JSONparsing期间调用哪个方法:

 fabric.NamedImage.fromObject = function(object, callback) { fabric.util.loadImage(object.src, function(img) { callback && callback(new fabric.NamedImage(img, object)); }); }; 

我们在这里加载一个图像(来自“object.src”),然后创build一个fabric.NamedImage的实例。 注意到那个时候,构造函数已经在处理“name”设置了,因为之前我们覆盖了“initialize”方法。

我们还需要指定fabric.NamedImage是一个asynchronous的“类”,意思是它的fromObject不返回一个实例,而是传递给一个callback:

 fabric.NamedImage.async = true; 

现在我们可以尝试这一切:

 // create image element var img = document.createElement('img'); img.src = 'https://www.google.comhttp://img.dovov.comsrpr/logo3w.png'; // create an instance of named image var namedImg = new fabric.NamedImage(img, { name: 'foobar' }); // add it to canvas canvas.add(namedImg); // save json var json = JSON.stringify(canvas); // clear canvas canvas.clear(); // and load everything from the same json canvas.loadFromJSON(json, function() { // making sure to render canvas at the end canvas.renderAll(); // and checking if object's "name" is preserved console.log(canvas.item(0).name); }); 

更简单的方法是添加属性post-stringify:

 var stringJson = JSON.stringify(this.canvas); var objectJson = JSON.parse(string.Json); //remove property1 property delete objectJson.property1; //add property2 property delete objectJson.property2; // stringify the object again stringJson = JSON.stringify(objectJson); // at this point stringJson is ready to be sent over to the server $http.post('http://serverurl/',stringJson); 

我有同样的问题,但我不想扩展fabric.js类。

我写了一个函数,它将参数中的布料canvas返回到我的特殊属性的string版本:

 function stringifyCanvas(canvas) { //array of the attributes not saved by default that I want to save var additionalFields = ['selectable', 'uid', 'custom']; sCanvas = JSON.stringify(canvas); oCanvas = JSON.parse(sCanvas) ; $.each(oCanvas.objects, function(n, object) { $.each(additionalFields, function(m, field) { oCanvas.objects[n][field] = canvas.item(n)[field]; }); }); return JSON.stringify(oCanvas); } 

当我使用canvas.loadFromJSON() ,特殊的属性似乎正确导入,我正在使用结构1.7.2