我希望能够将当前canvas的状态保存为服务器端数据库(可能作为JSON字符串),然后再使用进行恢复loadFromJSON。通常,这很容易使用以下方法完成:
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方法在要添加到画布上的结构对象上设置了一些自定义属性:
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字符串中保存 和 还原画布,并且还原的画布还将包含我的自定义属性?谢谢。
toObject
好问题。
如果要向对象添加自定义属性,则这些对象在某种程度上可能是“特殊的”。子类化似乎是一个合理的解决方案。
例如,这是我们将a子类fabric.Image化为命名图像的方法。这些图像对象然后可能具有“ Gandalf”或“ Samwise”之类的名称。
fabric.Image
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。
fabric.<type>.fromObject
fabric.NamedImage.fromObject
然后,我们覆盖initialize(构造函数)实例方法,以便在初始化对象时也设置“名称”属性(如果提供了该属性)。
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
并且我们还需要指定它fabric.NamedImage是一个异步“类”,这意味着它fromObject不返回实例,而是将其传递给回调:
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); });