web-dev-qa-db-ja.com

Fabric.js-カスタム属性でキャンバスをサーバーに保存する方法

現在のキャンバスの状態を、おそらく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文字列からキャンバスを保存の両方を復元できるように、この問題に取り組む良い方法は何ですか?カスタム属性も含まれますか?ありがとう。

51
sa125

良い質問。

オブジェクトにカスタムプロパティを追加する場合、それらのオブジェクトは何らかの形で「特別」である可能性があります。それらをサブクラス化するのは合理的な解決策のようです。

たとえば、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 });
  }
});

まず、これらのオブジェクトに型を指定します。このタイプはloadFromJSONfabric.<type>.fromObjectメソッドを自動的に呼び出すために使用されます。この場合、fabric.NamedImage.fromObjectになります。

次に、initialize(コンストラクター)インスタンスメソッドを上書きし、オブジェクトを初期化するときに "name"プロパティを設定します(そのプロパティが指定されている場合)。

次に、toObjectインスタンスメソッドを上書きして、返されたオブジェクトに "name"を含めます(これは、ファブリックでのオブジェクトのシリアル化の基礎です)。

最後に、前述の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のインスタンスを作成します。以前に「initialize」メソッドを上書きしたため、その時点で、コンストラクターが「name」設定をすでに処理していることに注意してください。

また、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);
});
68
kangax

ワオ。ここに何かが足りませんか?

私はこれを何度も行いましたが、派手なサブクラス化は必要ありません。

ドキュメントはそれをカバーしています: http://fabricjs.com/docs/fabric.Canvas.html#toJSON

ToJSON()の呼び出しに文字列としてプロパティ名の配列を含めるだけです。

例えば

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

うまくいけば....ボーナスポイントのために、カスタム属性を回復させるリバイバー機能を追加できます。

繰り返しますが、これはドキュメントで説明されており、例があります。

7
kbcool

同じ問題がありましたが、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()を使用すると、特殊な属性が適切にインポートされたように見えます。ファブリック_1.7.2_を使用しています。

3
adrien54

より単純なアプローチは、文字列化後にプロパティを追加することです。

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
Dan Ochiana

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()を呼び出すと、これらのプロパティは出力に保持されます。その後、JSON出力でcanvas.loadFromJSON()を使用すると、カスタム属性がインポートされ、Fabricオブジェクトに適用されます。

0
Weft