一尘不染

Fabric.js-如何使用自定义属性在服务器上保存画布

json

我希望能够将当前canvas的状态保存为服务器端数据库(可能作为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字符串中保存
还原画布,并且还原的画布还将包含我的自定义属性?谢谢。


阅读 1105

收藏
2020-07-27

共1个答案

一尘不染

好问题。

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

例如,这是我们将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(构造函数)实例方法,以便在初始化对象时也设置“名称”属性(如果提供了该属性)。

然后,我们覆盖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。请注意,到那时,构造函数将已经处理“名称”设置,因为我们之前覆盖了“初始化”方法。

并且我们还需要指定它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);
});
2020-07-27