63

我希望能够将当前画布的状态保存到服务器端数据库,可能是 JSON 字符串,然后使用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();
}

但是,我还使用内置Object#set方法在要添加到画布的织物对象上设置了一些自定义属性:

// 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();

问题是,当我检查服务器端的请求时,我发现我的自定义属性没有从画布中解析出来并与其他所有内容一起发送。这可能与方法如何toObject删除对象类中不是默认属性的任何内容有关。解决这个问题的好方法是什么,这样我就可以从服务器发送的 JSON 字符串中保存和恢复画布,并且恢复的画布也将包含我的自定义属性?谢谢。

4

5 回答 5

79

好问题。

如果您正在向对象添加自定义属性,那么这些对象可能在某种程度上是“特殊的”。似乎对它们进行子类化是一个合理的解决方案。

例如,下面是我们如何将 a 子类fabric.Image化为命名图像。然后,这些图像对象可以具有“Gandalf”或“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 });
  }
});

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

然后我们覆盖initialize(构造函数)实例方法,以便在初始化对象时也设置“name”属性(如果给定了该属性)。

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

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

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

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

我们还需要指定它fabric.NamedImage是一个异步“类”,这意味着它fromObject不返回实例,而是将其传递给回调:

fabric.NamedImage.async = true;

现在我们可以尝试一下:

// create image element
var img = document.createElement('img');
img.src = 'https://www.google.com/images/srpr/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);
});
于 2012-06-30T17:28:18.233 回答
19

哇。我在这里错过了什么吗?

我已经做过很多次了,它不需要任何花哨的子类化。

文档涵盖了它: http: //fabricjs.com/docs/fabric.Canvas.html#toJSON

只需在对 toJSON() 的调用中包含一组属性名称作为字符串。

例如

canvas.toJSON(['wizard','hobbit']);

希望.... 对于奖励积分,您可以添加一个 reviver 功能,该功能将补充您的自定义属性。

同样,这在文档中有所介绍,并有一个示例。

于 2018-04-21T10:16:52.587 回答
3

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

我编写了一个函数,该函数将织物画布作为参数并返回带有我的特殊属性的字符串化版本:

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(),我正在使用 fabric 1.7.2

于 2017-01-13T14:55:06.573 回答
2

更简单的方法是在字符串化后添加属性:

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);
于 2014-09-17T14:51:25.627 回答
2

如果不想在每次调用时都指定要使用的自定义属性canvas.toJSON(),并且不想使用复杂的子类化方法,这里有一个非常简单的扩展 Fabric 方法的toObject方法。

//EXTEND THE PROPS FABRIC WILL EXPORT TO JSON
fabric.Object.prototype.toObject = (function(toObject) {
    return function() {
        return fabric.util.object.extend(toObject.call(this), {
            id: this.id,
            wizard: this.wizard,
            hobbit: this.hobbit,
        });
    };
})(fabric.Object.prototype.toObject);

然后您可以在 Fabric 对象上设置自定义属性。

item.set("wizard","gandalf");
item.set("hobbit","bilbo");

当您调用canvas.toJSON()这些属性时,这些属性将持续输出。如果您随后使用canvas.loadFromJSON()JSON 输出,自定义属性将被导入并应用于 Fabric 对象。

于 2019-05-30T17:43:44.090 回答