Fabric.js - how to save a canvas on a server with custom attributes

I would like to save the current canvas state in the server database, possibly as a JSON string, and then restore it using loadFromJSON . This is usually easy to do using:

 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'); } 

And then

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

However, I also set a few custom attributes for the object objects that I add to the canvas using the built-in Object#set method:

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

The problem is that when I check the request on the server side, I see that my user attributes were not parsed from the canvas and sent along with everything else. This is likely due to the way the toObject method removes everything that is not the default attribute in the object's class. What would be a good way to solve this problem, so that I can save and restore the canvas from the JSON string sent by the server, and the restored canvas will also include my settings attributes? thank.

+43
json database savechanges fabricjs
Jun 30 2018-12-12T00:
source share
3 answers

Good question.

If you add custom properties to objects, these objects are most likely β€œspecial”. It seems that subclassing them would be a reasonable solution.

For example, here we will subclass fabric.Image into a named image. These image objects might have names such as Gandalf or Self Help.

 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 }); } }); 

First we give these objects a type. This type is used by loadFromJSON to automatically invoke the fabric.<type>.fromObject . In this case, it will be fabric.NamedImage.fromObject .

Then we overwrite the instance method initialize (constructor), also set the name property when initializing the object (if this property is set).

Then we overwrite the toObject instance method to include the "name" in the returned object (this is the cornerstone of serializing the object in the fabric).

Finally, we also need to implement this fabric.NamedImage.fromObject , which I mentioned earlier, so that loadFromJSON knows which method to call when parsing JSON:

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

We load the image here (from "object.src"), and then create an instance of fabric.NamedImage from it. Note that at this point, the constructor will already take care of setting the "name", since we previously rewritten the "initialize" method.

And we will also need to indicate that fabric.NamedImage is an asynchronous "class", meaning that its fromObject does not return an instance, but passes it to the callback:

 fabric.NamedImage.async = true; 

And now we can try everything:

 // 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 "name" is preserved console.log(canvas.item(0).name); }); 
+58
Jun 30 '12 at 17:28
source share

A simpler approach is to add post-stringify properties:

 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); 
+2
Sep 17 '14 at 14:51
source share

I had the same problem, but I did not want to extend fabric.js classes.

I wrote a function that takes a fabric canvas in a parameter and returns a string version with my special attributes:

 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); } 

Special attributes are usually imported when I use canvas.loadFromJSON() , I use 1.7.2 .

+2
Jan 13 '17 at 2:55 on
source share



All Articles