Fabric JS 在数据库中存储步骤

Fabric JS storing steps in database

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

您可以在它的最终位置(或初始位置,随便什么)对其进行序列化,然后保存步骤之间的增量,以便您每次进入历史记录 forward/back 时都可以使用 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 之类的操作

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%