如何从 JSON 重新加载自定义 fabricJS 画布对象?

Posted

技术标签:

【中文标题】如何从 JSON 重新加载自定义 fabricJS 画布对象?【英文标题】:How do I reload custom fabricJS canvas objects from JSON? 【发布时间】:2021-06-17 00:49:01 【问题描述】:

我正在使用 fabricJS 和自定义类。

我遇到了canvas.loadFromJSON() 的问题,所以我从another question 获取了一个基本的 MRE,以找出我的自定义结构类有什么问题。问题中的示例运行良好,但它创建了一个基于 fabric.Rect 的类。

我需要一个基于fabric.Polyline 的自定义类。我稍微修改了代码并得到了这个:

var canvas = new fabric.Canvas('c', 
);

// my custom TreeNode class
fabric.TreeNode = fabric.util.createClass(fabric.Polyline, 
    type: 'treeNode',

    initialize: function (options) 
        options || (options = );
        // console.log(options);

        this.callSuper('initialize', options);
        this.stroke = 'blue';
    ,

    toObject: function () 
        return fabric.util.object.extend(this.callSuper('toObject'), 
            stroke: this.get('stroke')
        );
    ,

    _render: function (ctx) 
        this.callSuper('_render', ctx);
    
);

fabric.TreeNode.fromObject = function (object, callback) 
    return fabric.Object._fromObject('TreeNode', object, callback);
;


var rect = new fabric.Rect(
    left: 300,
    top: 200,
    fill: 'blue',
    width: 50,
    height: 50,
);
canvas.add(rect);

var node = new fabric.TreeNode([ x: 50, y: 100 ,  x: 200, y: 300 ,  x: 10, y: 10 ]);
canvas.add(node);

// Serializing the whole canvas to JSON
var myJSON = JSON.stringify(canvas);

// console.log(myJSON);
// '"version":"4.3.1","objects":["type":"rect","version":"4.3.1","originX":"left","originY":"top","left":300,"top":200,"width":50,"height":50,"fill":"blue","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"rx":0,"ry":0,"type":"treeNode","version":"4.3.1","originX":"left","originY":"top","left":9.5,"top":9.5,"width":190,"height":290,"fill":"rgb(0,0,0)","stroke":"blue","strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"points":["x":50,"y":100,"x":200,"y":300,"x":10,"y":10]]'


// De-serializing doesn't work if fabric.TreeNode object is part of canvas
canvas.loadFromJSON(myJSON, canvas.renderAll.bind(canvas));
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.js"
        integrity="sha512-CzyxOXSECwo2nGJQZ2P8fdDxWVMVzn0jNdT2lYJ3afbqDJbaQ4qxgv9sLc85f6KP8HO2y929Bu+lnPKGC3ofSg=="
        crossorigin="anonymous"></script>

</head>

<body>
    <canvas id="c"   style="border: 1px solid rgb(219, 40, 40);"></canvas>
    <!-- <script src="./loadJSON.js"></script> -->
</body>

</html>

如您所见,当尝试从 JSON 加载画布时,我得到一个 Uncaught TypeError: Cannot read property 'x' of undefined at find (fabric.js:2469) at min (fabric.js:2444)

在浏览器上,我可以检查源代码中的错误来源,但我无法弄清楚问题所在。非常感谢任何帮助。

【问题讨论】:

【参考方案1】:

我在 fabricJS gitHub 页面 (link here) 上发布了我的问题, @asturur 非常友好地指出我必须更改 fromObject 函数。

折线的fromObject具体是:

>   fabric.Polyline.fromObject = function(object, callback) 
>     return fabric.Object._fromObject('Polyline', object, callback, 'points');   ;

尝试使用正确的初始化函数和 fromObject 函数,它引用最后一个参数上的点,我 假设它可以解决您的问题

所以,话虽如此,我修改了我的 sn-p,现在 loadFromJSON() 工作得很好。这里的例子:

var canvas = new fabric.Canvas('c', 
);

// my custom TreeNode class
fabric.TreeNode = fabric.util.createClass(fabric.Polyline, 
    type: 'treeNode',

    initialize: function (options) 
        options || (options = );
        // console.log(options);

        this.callSuper('initialize', options);
        this.stroke = 'blue';
    ,

    toObject: function () 
        return fabric.util.object.extend(this.callSuper('toObject'), 
            stroke: this.get('stroke')
        );
    ,

    _render: function (ctx) 
        this.callSuper('_render', ctx);
    
);

fabric.TreeNode.fromObject = function (object, callback) 
    // ** CHANGE : using the _fromObject() of Polyline
    return fabric.Object._fromObject('Polyline', object, callback, 'points');
;


var rect = new fabric.Rect(
    left: 300,
    top: 200,
    fill: 'blue',
    width: 50,
    height: 50,
);
canvas.add(rect);

var node = new fabric.TreeNode([ x: 50, y: 100 ,  x: 200, y: 300 ,  x: 10, y: 10 ]);
canvas.add(node);

// Serializing the whole canvas to JSON
var myJSON = JSON.stringify(canvas);

console.log(myJSON);
// "version":"4.3.1","objects":["type":"rect","version":"4.3.1","originX":"left","originY":"top","left":300,"top":200,"width":50,"height":50,"fill":"blue","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"rx":0,"ry":0,"type":"treeNode","version":"4.3.1","originX":"left","originY":"top","left":9.5,"top":9.5,"width":190,"height":290,"fill":"rgb(0,0,0)","stroke":"blue","strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"points":["x":50,"y":100,"x":200,"y":300,"x":10,"y":10]]


// De-serializing now works for both rect and TreeNode objects
canvas.loadFromJSON(myJSON, canvas.renderAll.bind(canvas));
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.js"
        integrity="sha512-CzyxOXSECwo2nGJQZ2P8fdDxWVMVzn0jNdT2lYJ3afbqDJbaQ4qxgv9sLc85f6KP8HO2y929Bu+lnPKGC3ofSg=="
        crossorigin="anonymous"></script>

</head>

<body>
    <canvas id="c"   style="border: 1px solid rgb(219, 40, 40);"></canvas>
    <!-- <script src="./loadJSON.js"></script> -->
</body>

</html>

【讨论】:

以上是关于如何从 JSON 重新加载自定义 fabricJS 画布对象?的主要内容,如果未能解决你的问题,请参考以下文章

在 fabricjs 对象上应用 JSON.stringify 后自定义属性丢失

使用fabricjs从json到图像

在 Fabric.js 中重置画布并使用 JSON 重新加载

如何从自定义 UITableViewCell 调用操作以重新加载 tableView

Fabric js从JSON加载画布

如何将自定义数组保存/重新加载到 plist