Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fabricjs: retain the correct indexes of an object's image filters after loadFromJSON

I am applying filters to objects (following image filters demo) and everything is ok but after I save and load the canvas, the image filters change index.

At the moment I have four filters and they are applied by index (as shown in the demo).

0 : Grayscale

1 : Invert

2 : Remove Color

3 :- Blend Color

So if I apply Grayscale, and Remove Color, the 'filters' array looks like this , with indexes 0 and 2 which is correct...

enter image description here

But after I load the canvas (using loadFromJSON), the object's 'filters' array looks like this, with the indexes reset...

enter image description here

Is there any way that I can load the object and retain the filter indexes? There is code that is dependant on this and it is causing errors when I load a canvas that has objects with filters.

I have tried applying the following upon creation of the object...

   oImg.filters = [
     false,
     false,
     false,
     false
   ];;

It works ok when the object is created...

enter image description here

But when it is loaded, the false indexes are removed and its the same result...

enter image description here

like image 701
Serks Avatar asked Mar 16 '18 05:03

Serks


2 Answers

I managed to get this working by changing the way I applied and retrieved the filters (by type rather than index). I simply checked to see if the filter existed (by 'type' not index), then spliced the filter at the desired index.

Changed this function...

getFilter(index) { 
   var obj = canvas.getActiveObject();
   return obj.filters[index];
}

to this...

getFilter(type) { 
  var obj = canvas.getActiveObject();
  if (obj) {
    filter = null;
    obj.filters.forEach(function(f) {
      if (f.type == type) {
        filter = f;
      }
    });
    return filter;
  }
}

Changed this function...

applyFilter(index, filter) {
  var object = canvas.getActiveObject();
  object.filters[index] = filter;
  object.applyFilters();
  canvas.renderAll();
}

to this...

applyFilter(type, filterIndex, filter) {
  var obj = canvas.getActiveObject();
  var indexExists = false;
  var filterFound = false;

  if (obj) {
    obj.filters.forEach(function(f, i) {
      if (f.type == type) {
        filterFound = true;
        obj.filters[i] = filter;
      } 
      if (filterIndex == i) {
        indexExists = true;
      } 
    });

    if (!filterFound && indexExists) {
      obj.filters.splice(filterIndex, 0, filter);
    } else if (!filterFound && !indexExists) {
      obj.filters[filterIndex] = filter;
    }

  }
  obj.applyFilters();
  canvas.renderAll();
}

Changed this function...

applyFilterValue(index, prop, value) {
  var obj = canvas.getActiveObject();
  if (obj.filters[index]) {
    obj.filters[index][prop] = value;
    obj.applyFilters();
    canvas.renderAll();
  }
}

to this...

applyFilterValue(type, filterIndex, prop, value) {
  var obj = canvas.getActiveObject();
  if (obj) {
    obj.filters.forEach(function(f, i) {
      if (f.type == type) {
        obj.filters[i][prop] = value;
      }
    });
  }
  obj.applyFilters();
  canvas.renderAll();
}
like image 148
Serks Avatar answered Sep 29 '22 02:09

Serks


In my current project i need to have the ability to customize the order of the filters. The problem with lost indexing after canvas.toDatalessJSON() gave me the idea to make a simple solution for that. I simply build a new array of filternames every time og load the canvas.

const filterNamesDefault = ['Contrast', 'Saturation', 'Sharpen', 'Tint', 'invert', 'grayscale', 'Sepia'];

var filterNames = [];
var canvasLoaded = false;

canvasLoaded is a boolean that has to be set to true when a canvas has been loaded. Then in the applyFilterType and applyFilterValue functions i provide the functions with the filter type instead of the index:

function filterIndex(filterType) {
      if (canvasLoaded) { return filterNames.indexOf(filterType); } else { return filterNamesDefault.indexOf(filterType); } 
} 

function applyFilterType(type, filter) {
     var index=filterIndex(type); 
     ...
}

 function applyFilterValue(type, prop, value) {
     var index=filterIndex(type);
     ...
}

Let's say i add some filters ['Sharpen','Tint'] that are stored in the canvas.filters object as usual, and after canvas save/load are repositioned. Then when i want to display the filter edit box for a image layer i execute this:

var obj = canvas.getActiveObject();
filterNames = [];
obj.filters.forEach(function(f,i){
    filterNames.push(f.type);
});
for (var i=0;i<filterNamesDefault.length;i++){
    if (!filterNames.includes(filterNamesDefault[i])) filterNames.push(filterNamesDefault[i]);
}

Now the new order for filterNames is:

['Sharpen', 'Tint', 'Contrast', 'Saturation', 'invert', 'grayscale', 'Sepia']

And it works as well with the new order of the filters.

like image 43
Flemming Avatar answered Sep 29 '22 00:09

Flemming