Подтвердить что ты не робот

Fabric.js - как сохранить холст на сервере с пользовательскими атрибутами

Я хотел бы сохранить текущее состояние canvas в серверной базе данных, возможно, как строку 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, отправленной сервером, а восстановленный холст также будет включать в себя мои настройки атрибуты? спасибо.

4b9b3361

Ответ 1

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

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

Например, здесь мы будем подклассифицировать 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.

Затем мы перезаписываем метод экземпляра initialize (constructor), также устанавливаем свойство name при инициализации объекта (если это свойство задано).

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

И нам также нужно будет указать, что 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 "name" is preserved
  console.log(canvas.item(0).name);
});

Ответ 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);

Ответ 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.