【问题标题】:Fabric JS storing steps in databaseFabric JS在数据库中存储步骤
【发布时间】:2021-08-04 02:20:01
【问题描述】:

这个问题可能有点自以为是,对此提前表示歉意。有什么方法可以有效地存储在 FabricJS 画布上完成的步骤?假设我是一个用户,我想稍后继续编辑我的画布,所以我关闭了窗口,下次打开它时,我们可以从数据库中加载所有之前的步骤吗?我正在考虑将canvas.toJSON() 存储到数据库中,但它非常大,比我需要的要大得多,因为所做的更改通常非常小。是否有任何更短且“可加载”到画布的功能或约定?我正在考虑为此创建自己的约定,仅存储实际更改,即增量,这是个好主意吗?

【问题讨论】:

    标签: javascript reactjs fabricjs


    【解决方案1】:

    您可以在其最终位置(或初始位置,等等)对其进行序列化,然后保存步骤之间的增量,以便您每次在历史记录中前进/后退时都可以使用 Object.assign()

    let superBigObj = {a: 100, b: 200, c: 300, d: {hello: "world"}};
    
    let smallObj = Object.assign(superBigObj, {c: 400});
    
    // smallObj is now: {a: 100, b: 200, c: 400, d: {hello: "world"}};
    

    问题是您需要编写一个重复数据删除程序来递归删除相同的属性(或仅提取更改的属性)。

    如果您担心数据库中的大小,那么您可以对它执行 atobbtoa 之类的操作来对其进行 base64 处理

    【讨论】:

    • 如果我只坚持使用Object.assign() 我不会创建任何重复的属性,对吧?或者你的意思是如果我不小心将相同的属性名称分配给了两个不同的东西?
    • 同样使用这种方法,我将不得不发明自己的存储和加载约定,对吧?
    • @Alien13 right -- 存在的属性将被新的属性覆盖(因此c 将变为400)。将添加一个存在但另一个不存在的东西。在处理嵌套对象时这很草率,但这可能是一个开始。另一个答案看起来更特定于 Fabric。是的,您必须发明一种存储/加载机制。通过删除对象来保存增量,然后 .assign() 该增量以重新组合您删除的内容。但警告:这不会很高效。
    【解决方案2】:

    TL;DR;

    您可以删除不必要的(默认)对象数据并使用lz-string进行压缩


    结果如此长的原因是它为您提供了所有属性,包括默认值。因此,您可以通过删除默认值来解决这个问题。

    此示例脚本会删除所有值为 0null 的对象属性。

    var canvas = new fabric.Canvas('c');
    canvas.add(new fabric.Rect({left: 50,  top: 50, height: 20, width: 20,  fill: 'green'}));
    canvas.add(new fabric.Circle({left: 100,  top: 100, radius: 50, fill: 'red'}));
    
    let output = canvas.toJSON();
    let copy = {...output, objects:[]};
    
    (output.objects || []).map(object => {
      let objectCopy = {};
      for (let key in object) {
        val = object[key];
        if (val !== null && val !== 0){
            objectCopy[key] = object[key]
        } else {
            // console.log('skipped', key, object[key])
        }
      }
      copy.objects.push(objectCopy);
    })
    var canvas2 = new fabric.Canvas('d');
    canvas2.loadFromJSON(JSON.stringify(canvas));
    
    console.log(JSON.stringify(canvas).length);
    console.log(JSON.stringify(copy).length);
    #canvas-wrapper {
      position: relative;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js" integrity="sha512-ACqMrfAtm537AWzgx/xQ57JnFxXeq8RylQMGg4y/e6M2ew4Z8NycE8aId/Bt2ZE+w1gNsox3MgwxKl7SGMRdtA==" crossorigin="anonymous"></script>
    <div id="canvas-wrapper"><canvas id="c" width="400" height="200" /></div>
    <div id="canvas-wrapper"><canvas id="d" width="400" height="200" /></div>

    这给了我们大约 20% 的改进。

    此外,这种方法很幼稚,还有很大的改进空间。它无法处理组(嵌套对象),如果您将默认值 1 覆盖为 0(即strokeWidth),它将忽略它?并导致错误的结果。因此,该解决方案必须考虑每种对象类型的完整默认设置列表,但它可以很容易地为您提供超过 50% 的常用压缩率。

    另一个选项是使用压缩库(如lz-string)来压缩你的字符串结果。这可以开箱即用地为您带来 %70-%80 的改进。这更快,更可靠。

    恕我直言,理想的情况是两者兼而有之,这可以使您降低到原来的 5-10%

    【讨论】:

    • 谢谢丹尼尔,我试过你的例子,它工作得很好。我对“另一种方法”有 2 个问题 1. 它支持嵌套对象吗? 2. 让canvas读取到lz-string压缩过的JSON容易吗?那将如何运作?
    • 另外一个题外话问题,如果我想说将所有这些步骤存储在某个唯一标识符下,例如 canvas_session_id ,那么最好的数据库存储架构是什么?我正在考虑键值存储。但也许不是最佳选择,因为我假设在我的用例中读取和写入将同样频繁。
    • 如果您在存储之前压缩,您可以在加载数据之前解压缩。压缩有助于存储和传输。
    • 如果你以压缩格式存储它,键值应该没问题,如果你将它发送到服务器进行数据存储,你可以做一个去抖动,数据保存在本地和上传的频率较低。如果您希望保留每个步骤,则需要存储每个步骤。
    猜你喜欢
    • 1970-01-01
    • 2023-03-30
    • 2019-09-02
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多