Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filters don't apply after load canvas from JSON #365

Closed
xxorax opened this issue Dec 29, 2012 · 11 comments · Fixed by #3210
Closed

Filters don't apply after load canvas from JSON #365

xxorax opened this issue Dec 29, 2012 · 11 comments · Fixed by #3210

Comments

@xxorax
Copy link
Contributor

xxorax commented Dec 29, 2012

When you save and load the canvas in JSON, the filters on the objects don't be apply until you call an async renderAll on the canvas, by refreshing it with canvas.renderAll() or by clicking inside the canvas.

See an exemple :
http://www.xorax.info/labs/misc/canvas/fabricjs-issue-loadAndFilter.php

Apply the greyscale filter, click save to save the JSON in the input, and

  • click to Load button to load the JSON in the input and click on the canvas to refresh it
  • or click to Load and refresh button to load and call Load and refresh after 500ms

It seems there is an async break somewhere.

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

@xxorax
Copy link
Contributor Author

xxorax commented Dec 29, 2012

Just found fabric.Image.prototype.initialize() call fabric.Image.prototype.applyFilter() without passing callback and if isLikelyNode is false, the replacement image maked by the filter is replaced asynchronously.

Si, hard to fix without passing callback arg to fabric.Image.prototype.initialize, maybe it would be necessary.to use a Deferred object like jQuery does.

@xxorax
Copy link
Contributor Author

xxorax commented Dec 30, 2012

Note there is a standalone implementation of $.Deferred : https://github.com/warpdesign/Standalone-Deferred

@kangax
Copy link
Member

kangax commented Dec 30, 2012

The way I implemented this in our app is along these lines:

canvas.loadFromDatalessJSON(json, function() {
  applyImageFilters();

and

applyImageFilters: function() {
  canvas.forEachObject(function(obj) {
    if (obj.type === 'image' && obj.filters.length) {
      obj.applyFilters(function() {
        obj.canvas.renderAll();
      });
    }
  });
}

Not sure if this should be part of loadFrom(Dataless)JSON

@xxorax
Copy link
Contributor Author

xxorax commented Dec 30, 2012

With this code, the canvas is rendered for each image object which has filter, instead of only at the end of all filters applied, no ?
I've updated the example to add "Load and refresh at end" button:

fcanvas.loadFromJSON($('#saved').val(), function () {
    var objLen = fcanvas.getObjects().length;
    var renderIfAll = function () {
        if (--objLen == 0) {
            fcanvas.renderAll();
        }
    };
    fcanvas.forEachObject(function(obj) {
        if (obj.type === 'image' && obj.filters.length) {
            obj.applyFilters(renderIfAll);
        } else {
            renderIfAll();
        }
    });
});

But an other issue is that the filters are applied 2 times in this case, once in fabric.Image.prototype.initialize and once here, and the rendering too.

@kangax
Copy link
Member

kangax commented Jan 1, 2013

Possible solutions:

  1. this.canvas.renderAll() in applyFilters callback from within initialize. This solves the problem of outdated canvas rendering and only calls applyFilters once. However, it's N render calls instead of 1.

  2. call applyFilters at the end of loadFromDatalessJSON once, without calling it in initialize. This solves the outdated canvas and avoids N render calls. But then we can't applyFilters when instantiating image.

Sent from my iPhone

On Dec 30, 2012, at 23:06, xxorax [email protected] wrote:

With this code, the canvas is rendered for each image object which has filter, instead of only at the end of all filters applied, no ?
I've updated the example to add "Load and refresh at end" button:

fcanvas.loadFromJSON($('#saved').val(), function () {
var objLen = fcanvas.getObjects().length;
var renderIfAll = function () {
if (--objLen == 0) {
fcanvas.renderAll();
}
};
fcanvas.forEachObject(function(obj) {
if (obj.type === 'image' && obj.filters.length) {
obj.applyFilters(renderIfAll);
} else {
renderIfAll();
}
});
});
But an other issue is that the filters are applied 2 times in this case, once in fabric.Image.prototype.initialize and once here, and the rendering too.


Reply to this email directly or view it on GitHub.

@jaydenseric
Copy link

This ugly fix solves the problem for anyone lurking:

$(window).on('load', function() {
    canvas.renderAll();
});

@jaydenseric
Copy link

Came across this old chestnut again today. Any progress on this? Need a solution that works as things are right now even if it is not optimal regarding render calls.

@jaydenseric
Copy link

For now I am using:

setTimeout(function() {
    canvas.renderAll();
}, 2000);

In the callback for loadFromJSON.

@DeviantDog
Copy link

I am using the following:

canvas.loadFromJSON(json), canvas.renderAll.bind(canvas), function(o, object) {
       if (object.type === 'image' && object.filters.length) {
            object.applyFilters(function() {
                  object.canvas.renderAll();
            });
       }
});       

@jitendrapawar
Copy link

jitendrapawar commented May 9, 2016

How to apply resize filters(lanczos etc...) in loadFromJSON()?

@asturur
Copy link
Member

asturur commented Aug 27, 2016

#3210

I made this:
The callback of loadFromObject that we uses to trigger the "objectLoaded" event and then count all the object restored, is passed down to applyfilters and called from there.
At this point the object will take longer to restore ( add filter rendering time ) but that should solve the callback issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants