TypeError occurs when calling toDataURL after image filter applied

233 Views Asked by At

Version

fabricjs 2.0.0-beta.6 node-canvas 1.6.6 nodejs 6.11.0

Steps to reproduce

fabricjs on nodejs, 1.7.16 works fine with image filter, when update to 2.0.0, can't get it work

var jsonStr = '{"objects":[{"type":"circle","originX":"left","originY":"top","left":119,"top":64,"width":100,"height":100,"fill":"#6dcdd0","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":0.8,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"radius":50,"startAngle":0,"endAngle":6.283185307179586}]}';
var canvas = fabric.createCanvasForNode(600, 600);
canvas.loadFromJSON(jsonStr, function() {
    canvas.renderAll();
    var dataUrl = canvas.toDataURL();
    fabric.Image.fromURL(dataUrl, function(img) {
        var filter = new fabric.Image.filters.Sepia();
        img.filters.push(filter);
        img.applyFilters();
        canvas.add(img);
        var dataUrl = canvas.toDataURL();
});

Expected Behavior

canvas.toDataURL() returns dataurl

Actual Behavior

"TypeError: Image or Canvas expected", " at TypeError (native)", " at klass._render (/data/web/websites/adamlv/node_workspace/monet/node_modules/fabric/dist/fabric.js:18699:28)", " at klass.drawObject (/data/web/websites/adamlv/node_workspace/monet/node_modules/fabric/dist/fabric.js:12527:12)", " at klass.render (/data/web/websites/adamlv/node_workspace/monet/node_modules/fabric/dist/fabric.js:12475:14)", " at klass._renderObjects (/data/web/websites/adamlv/node_workspace/monet/node_modules/fabric/dist/fabric.js:6949:34)", " at klass.renderCanvas (/data/web/websites/adamlv/node_workspace/monet/node_modules/fabric/dist/fabric.js:6927:12)", " at klass.renderAll (/data/web/websites/adamlv/node_workspace/monet/node_modules/fabric/dist/fabric.js:8862:12)", " at klass.__toDataURLWithMultiplier (/data/web/websites/adamlv/node_workspace/monet/node_modules/fabric/dist/fabric.js:11226:14)", " at klass.toDataURL (/data/web/websites/adamlv/node_workspace/monet/node_modules/fabric/dist/fabric.js:11198:19)", " at Command.callback (/data/web/websites/adamlv/node_workspace/monet/api/canvas_converter.js:249:58)", " at normal_reply (/data/web/websites/adamlv/node_workspace/monet/node_modules/redis/index.js:721:21)", " at RedisClient.return_reply (/data/web/websites/adamlv/node_workspace/monet/node_modules/redis/index.js:819:9)", " at JavascriptRedisParser.returnReply (/data/web/websites/adamlv/node_workspace/monet/node_modules/redis/index.js:192:18)", " at JavascriptRedisParser.execute (/data/web/websites/adamlv/node_workspace/monet/node_modules/redis-parser/lib/parser.js:553:10)", " at Socket. (/data/web/websites/adamlv/node_workspace/monet/node_modules/redis/index.js:274:27)", " at emitOne (events.js:96:13)"

1

There are 1 best solutions below

0
On

While this is not a solving answer, it explains you what is going on. A solution is being worked on.

The JSDOM package is taking care of creating canvases with node-canvas integration for fabricjs.

writing document.createElement('canvas') should provide a fake canvas element in node, backed and wrapped with node-canvas.

Somehow this is not working correctly in fabric where one of the canvases is not wrapped in JSDOM. When drawImage is called from a context of a JSDOM canvas, the received first argument is unwrapped and the node-canvas is used instead of the html shimmer. It looks like this is not working as of now since the createCanvasForNode function is not exposing the correct context/canvas.

If you do not use it and just do: var fabric = require('fabric').fabric;

var jsonStr = '{"objects":[{"type":"circle","originX":"left","originY":"top","left":119,"top":64,"width":100,"height":100,"fill":"#6dcdd0","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":0.8,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"radius":50,"startAngle":0,"endAngle":6.283185307179586}]}';

var canvas = new fabric.StaticCanvas();
canvas.width = 600;
canvas.height = 600;
canvas.loadFromJSON(jsonStr, function() {
    canvas.renderAll();
    var dataUrl = canvas.toDataURL();
    fabric.Image.fromURL(dataUrl, function(img) {
        var filter = new fabric.Image.filters.Sepia();
        img.filters.push(filter);
        // img.applyFilters();
        canvas.add(img);
        canvas.renderAll();
        var dataUrl2 = canvas.toDataURL();
        console.log(dataUrl2);
    });
});

you will notice the script is working without the nodeCanvas but using plain browser code. Filtering is still broken.