Вопрос по fabricjs, database, json, savechanges – Fabric.js - как сохранить холст на сервере с пользовательскими атрибутами

48

Я хотел бы иметь возможность сохранить текущий холст. укажите базу данных на стороне сервера, вероятно, в виде строки 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 Метод удаляет все, что не является атрибутом по умолчанию в классе объекта. Что было бы хорошим способом решения этой проблемы, чтобы я могand восстановить холст из строки JSON, отправленной сервером, и восстановленный холст также будет включать мои пользовательские атрибуты? Благодарю.

Ваш Ответ

4   ответа
63

Хороший вопрос.

Если вы добавляете пользовательские свойства к объектам, эти объекты, скорее всего, "особенные". каким-то образом. Кажется, что их подклассы были бы разумным решением.

Например, вот как мы бы подклассfabric.Image в названное изображение. Эти объекты изображения могут иметь имена, подобные & quot; Гэндальфу & quot; или "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 (конструктор) метод экземпляра, чтобы также установить & quot; имя & quot; свойство при инициализации объекта (если это свойство задано).

Потом переписываем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 из этого. Обратите внимание, что на этом этапе конструктор уже позаботится о & quot; имени & quot; настройки, так как мы перезаписали "инициализировать" метод ранее.

И нам также нужно будет указать, что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);
});
Отличный ответ, и работал как шарм. Спасибо! sa125
Независимо от текущей реализации, похоже, я сам все испортил - посмотрите вот это:stackoverflow.com/a/11298971/187907 sa125
На самом деле, я получаю сообщение об ошибке при попытке загрузить холст обратно из строки JSON. Я используюcanvas.loadFromDatalessJSON(json), который ранее работал сfabric.Image, Теперь, когда я используюfabric.NamedImage как предложено выше, я получаюcannot call method 'setupState' of undefined, Как я могу это исправить? sa125
Кажется, ошибка вtoDatalessJSON : / У нас есть некоторая дублирующая логика в toJSON и toDatalessJSON, и я хочу переписать все это, сделав его красивым, чистым и последовательным. Будем делать когда-нибудь в ближайшее время.
@kangax, это действительно круто, но я изо всех сил пытаюсь сделать то же самое (добавить имя) для подклассаfabric.Group кажется, работает до такой степени, что я могу сохранить JSON, но при загрузке я получаюUncaught TypeError: Cannot read property 'async' of undefined Я довольно новичок в JS и поэтому, вероятно, получилfromObject(); неправильно. Вы случайно не знаете подпись?
2

Вот это да. Я что-то здесь упускаю?

Я делал это много раз, и он не нуждается в каких-либо причудливых подклассах.

Документы охватывают это:http://fabricjs.com/docs/fabric.Canvas.html#toJSON

Просто включите массив имен свойств в виде строк в ваш вызов toJSON ().

Например

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

Надеюсь .... для бонусных баллов вы можете добавить функцию оживления, которая будет регидрировать ваши пользовательские атрибуты.

Опять же, это описано в документации и имеет пример.

Теперь я понимаю, что этот вопрос 6 лет LOL. В любом случае, я надеюсь, что это поможет любому, кто споткнется.
Отличный ответ, сделал трюк очень быстро.
2

Более простым подходом было бы добавить свойства post-stringify:

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);
Обратное должно работать без проблем:github.com/kangax/fabric.js/issues/471
3

У меня была та же проблема, но я не хотел расширять классы 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.

Супер лайк, хорошо работает ...

Похожие вопросы