2012-06-30 18 views
44

Ich möchte in der Lage sein, den aktuellen Canvas-Status in einer serverseitigen Datenbank zu speichern, wahrscheinlich als JSON-String, und ihn später mit loadFromJSON wiederherzustellen . Typischerweise wird dies leicht erreicht werden:Fabric.js - So speichern Sie Canvas auf dem Server mit benutzerdefinierten Attributen

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

Und dann

function loadCanvas(json) { 

    // parse the data into the canvas 
    canvas.loadFromJSON(json); 

    // re-render the canvas 
    canvas.renderAll(); 

    // optional 
    canvas.calculateOffset(); 
} 

Allerdings habe ich auch ein paar eigene worden Einstellung Attribute auf die textilen Objekten ich auf die Leinwand Zugabe bin mit dem builtin Object#set Methode:

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

Das Problem ist, dass, wenn ich die Anfrage auf der Server-Seite zu überprüfen, ich sehe, dass meine benutzerdefinierten Attribute nicht von der Leinwand analysiert wurden und schickten zusammen mit alles andere. Dies hat wahrscheinlich damit zu tun, wie die Methode toObject alles entfernt, was kein Standardattribut in der Objektklasse ist. Was wäre ein guter Weg, um dieses Problem anzugehen, so dass ich und sowohl die Canvas aus einer JSON-Zeichenfolge vom Server gesendet speichern kann, und die wiederhergestellte Leinwand wird auch meine benutzerdefinierten Attribute enthalten? Vielen Dank.

Antwort

59

Gute Frage.

Wenn Sie Objekten benutzerdefinierte Eigenschaften hinzufügen, sind diese Objekte wahrscheinlich in irgendeiner Weise "speziell". Es scheint, als wäre es eine vernünftige Lösung, sie zu untergliedern.

Zum Beispiel, wie wir eine fabric.Image in ein benanntes Bild unterklassieren würden. Diese Bildobjekte könnten dann Namen wie "Gandalf" oder "Samweis" haben.

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

Zuerst geben wir diesen Objekten einen Typ. Dieser Typ wird von loadFromJSON verwendet, um automatisch die Methode fabric.<type>.fromObject aufzurufen. In diesem Fall wäre es fabric.NamedImage.fromObject.

Dann überschreiben wir die Instanzmethode initialize (Konstruktor), um auch die Eigenschaft "name" beim Initialisieren eines Objekts zu setzen (wenn diese Eigenschaft angegeben wird).

Dann überschreiben wir die Instanzmethode toObject, um "name" in das zurückgegebene Objekt aufzunehmen (dies ist ein Eckpfeiler der Objektserialisierung in Fabric).

Schließlich wir müssen auch implementieren, dass fabric.NamedImage.fromObject, die ich bereits erwähnt, so dass loadFromJSON würde wissen, welche Methode bei JSON Parsen aufzurufen:

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

Wir laden Sie ein Bild hier (aus " object.src ") und dann eine Instanz von fabric.NamedImage daraus erstellen. Beachten Sie, dass zu diesem Zeitpunkt der Konstruktor bereits die Einstellung "name" übernimmt, da wir die Methode "initialize" früher überschrieben haben.

Und wir werden auch angeben müssen, dass fabric.NamedImage ist ein asynchrones „Klasse“, meanining, dass seine fromObject keine Instanz zurückgibt, sondern übergibt sie an einen Rückruf:

fabric.NamedImage.async = true; 

Und nun können wir versuchen, dies alles aus:

// 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); 
}); 
+0

Ausgezeichnete Antwort und arbeitete wie ein Charme. Vielen Dank! – sa125

+1

Ich erhalte tatsächlich einen Fehler, wenn ich versuche, die Leinwand aus der JSON-Zeichenfolge zurück zu laden. Ich benutze 'canvas.loadFromDatalessJSON (json)', das vorher mit 'fabric.Image' gearbeitet hat. Nun, da ich das 'fabric.NamedImage' verwende, wie oben vorgeschlagen, bekomme ich' 'kann nicht Methode 'setupState' von undefined aufrufen. Wie kann ich das beheben? – sa125

+1

Scheint ein Fehler in 'toDatelessJSON' zu sein:/Wir haben eine duplizierende Logik in toJSON und toDatelessJSON und ich möchte das alles neu schreiben, was es nett, sauber und konsistent macht. Wird irgendwann in naher Zukunft tun. – kangax

2

Ein einfacher Ansatz wäre es, die Eigenschaften nach stringify hinzuzufügen:

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); 
+0

Die Umkehrung sollte ohne Probleme funktionieren: https: // github.com/kangax/fabric.js/Ausgaben/471 – xmojmr

2

Ich hatte das gleiche Problem, aber ich wollte die Klassen von fabric.js nicht erweitern.

Ich schrieb eine Funktion, die den Stoff Leinwand in Parameter nimmt und gibt eine Zeichenfolgeversion mit meiner speziellen Eigenschaften:

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

Die besonderen Attribute richtig importiert scheint, wenn ich canvas.loadFromJSON() verwenden, ich bin mit Stoff 1.7.2.